Solidity Basics

Hi @ecbwong,

That’s right

Yes

Correct

That’s correct … here you are talking about state variables, which are saved persistently in storage, which is another specific data location. All values stored in state variables are automatically saved in storage, so there is no need to explicity declare their data location with storage. It’s only necessary to declare the data location of local variables, parameters, and return values which are defined as complex data types (strings, arrays and struct instances). This is because, in these specific cases, there is a choice of data location. State variables, local variables, parameters and return values defined with any of the other data types (addresses, Booleans, integers or unsigned integers) have their data location automatically assigned by default, so in these cases there is no need to declare the data location.

There is a specific section of this course which introduces the different types of data location, and the circumstances under which it needs to be explicitly declared. So, you’ll get to see more examples and to practise using it in an assignment.

Thanks very much Jonathan. Very precise answers to each and every question. Appreciate it.

1 Like

I would like a plutus course as well, you have any experience with it @danielmain?

1 Like

Would any of you be so nice as to answer this question for me?:

Why doesn’t the .push function work for me?

 address admin1;
     address admin2;
     address admin3;
     address owner;
    address[] admins;
    function push()private{
        admins[].push([owner,admin1,admin2,admin3]);
    }

Line seven always has a pop up that says "push not found " and “Index expression cannot be ommited”
I haave no idea what those mean :woozy_face: :face_with_raised_eyebrow:

Hi Jonathan

Appreciate very much if you could clarify certain matters in Solidity Basic as regards to the topic on Struct.

I will use Filip video and his codes are as follows

A). Struct Person {
unit age ;
string name;
}

I understand this part being the object blueprint for future object creation.

B). Person [ ] people;

I am ok with this. An array of people variables of Person object.

C). I am lost in his Function addPerson. The body of the function goes like this

Person memory newPerson = Person ( 30, " Bob");
people.push( newPerson);

I am utterly confused with the use of the wordings Person, newPerson and people. In part B, since people variables is the name for Person Class, shouldn’t it be written as

Person memory people = people ( 30, “Bob”);
Person.push ( people);

Could you help me out on Solidity syntax on this because I couldn’t figure out why Filip code in the way he did. I haven’t touched on his getPerson function which is unclear to me too.

Thanks
Eric

1 Like

Hey @jakerichguy,

There are several things wrong with your code …

To push values to an array, you need to call the push method on the array name without the square brackets …

admins.push();

To reference a value within an array we append its index value in square brackets to the array name e.g. admins[0] will reference the first value in the array (at index 0). So, you are getting the error message   Index expression cannot be omitted   because the compiler thinks you want to reference an address within the admins array, and so it’s expecting an index number after the opening square bracket.

Once you remove the square brackets as I’ve shown you above, you’ll now get the error message …

Member "push" not found or not visible after argument-dependent lookup in address[] storage ref.

It’s not clear what is meant by argument-dependent lookup, but essentially, the compiler is informing you that you can’t push an array value …
[owner, admin1, admin2, admin3]
… to the admins array (the address[ ] storage reference). This is because admins is defined as an array containing individual address values e.g.

// address[] admins
[0x74...483, 0x38...819, ...]

… and not as an array containing further arrays of address values e.g.

// address[][] admins
[
   [0x74...483, 0x38...819],
   [0x32...715, 0x44...588],
   ...
] 

Before you remove the square brackets from the array name admins (as above), you also get a slightly different version of this error message. Here, the compiler still thinks you are referencing (looking up) a specific address held in the admins array, and I think it’s basically telling you that you can’t push a value to an address: .push() is an array method that can only be called on an array.

However, even if you also remove the square brackets from the 4 addresses you want to push to the array, you’ll still get the same error message…

admins.push(owner, admin1, admin2, admin3);  // => Error

This is because, as far as I’m aware, you can only use the push method to push a single value at a time to an array e.g.

admins.push(admin1);

The following works, but it is obviously very repetitive and inefficient to code it like this …

admins.push(owner);
admins.push(admin1);
admins.push(admin2);
admins.push(admin3);

As you will already need to have assigned address values to the state variables owner, admin1, admin2 and admin3, my question to you would be: Why do you need to store these addresses in separate state variables AND in an array? Such a duplication of data saved in storage is unnecessarily expensive. I would also think about the following:

  • If you need to store these addresses together in an array, then why not just assign them directly to admins, and remove the need for the separate address state variables altogether?

  • If there is a fixed number of admin addresses that you need to store together in a data structure, then you should define the array as a fixed-sized array instead of dynamically-sized (e.g. address[4] admins;) and assign the admin addresses using index numbers instead of the push method. Or you could think about using a struct instead of an array.

Let us know if anything is unclear, or if you have any further questions. And if your code relates to the Multi-Sig project, then please post your questions in the Project - Multisig Wallet discussion topic instead of here. :slight_smile:

1 Like

Hey Eric,

Exactly right … your “blueprint” analogy is a good one and helpful. I often use the word “template”, which is essentially the same. This “blueprint” that is defined in the struct creates what we can consider to be a customised data-type. So, just as we can declare a variable with a uint or string data-type, for example, we can also declare a variable with a customised data-type using the struct name — in our case, with Person. It’s like a data-type, because it is defining the type of value that the variable can hold: in our case a struct instance (like an object) containing 2 properties (age holding a uint value, and name holding a string value).

This helps us to understand …

… which is a array defined as containing instances (values) created from the Person “blueprint”.

This isn’t correct …
people is the name of the array, which contains instances based on our Person “blueprint” (or Person class, as you have called it here).

To create an instance based on a struct “blueprint”, we need to use the name of the struct. So, this is why we use Person and not the name of the array people in the following line …

We can create a struct instance in a single expression using parentheses. The values assigned to the properties must be placed between the parentheses in the same order as the properties are defined within the struct. This is why we don’t need to declare the property names. As age is the first property within the Person struct, 30 will be automatically assigned to the new instance’s age property etc.

Within the the body of the addPerson function, in the first line (quoted above) a new Person instance is created and assigned to a local variable called newPerson. We could call this local variable anything, apart from the array name people — we are not referencing the array in the first line of code. As you know, all variables in Solidity need to be declared with a specific data-type. Because this local variable is temporarily storing our new Person instance, we need to declare it with our customised data-type, Person . As a struct instance is a complex data-type, we also need to declare the data location of any local variable holding a struct instance.

In this second line we are pushing the newly created Person instance (stored temporarily in the newPerson variable) to the people array. As in JavaScript, the push method is appended to the array it is called on using dot notation. The value to be pushed to (added to the end of) the array is the push method’s argument, and so is placed within the parentheses.

This is wrong, because…

  1. .push() is an array method, and so it can’t be called on a struct. The properties of a struct are fixed according to its definition (a fixed “blueprint”). We can’t add, remove or modify a struct’s properties. We can only modify the property values of individual instances created from the struct “blueprint”.
  2. It’s trying to do the opposite of what we want to do.

Hopefully, this explanation makes things clearer and will also help you to understand the syntax in the getPerson function. But, just let me know if anything is still unclear, or if you have any more questions.

Hi Jonathan

Thanks for the very concise answers. I think I am getting it. Basically my confusion over when to use Person and people is answered in your statement - " To create an instance based on a struct “blueprint”, we need to use the name of the struct. So, this is why we use Person and not the name of the array people"

You also mentioned that ‘newPerson’, an instance of the Person class is a local variable inside the body of addPerson function. Am I right by saying that code

people.push( newPerson)

add the newPerson instance to the people array? Thus allowing getPerson function to retrieve the instances back when required? The newPerson being pushed to the array would be in ‘state’ variable storage?

Thanks
Eric

1 Like

Thank you so much @jon_m ! I think I’ll take your advice and use a struct instead. :+1:

1 Like

Hi Eric,

Glad the answers were helpful :slightly_smiling_face:

Yes … push() works in same way as it does in JavaScript: it adds the value between the parentheses to the end of the array. So, the first newPerson pushed to people will be at index 0 (people[0]), the second newPerson pushed will be at index 1 (people[1]) etc.

Yes … and the getPerson function works by retrieving specific instances according to their index position within the array e.g.

Person memory person = people[_index];
return(person.age, person.name);

or you could code it without using a local variable, as follows …

return(people[_index].age, people[_index].name);

The user calls getPerson() with the index position of the instance they want to retrieve.

Correct … the people array is a state variable and is saved in persistent storage.

Instead of pushing and storing the struct instances (created in addPerson) to an array, we can also assign them to a mapping. In a mapping, each instance would be mapped to a unique key (an address, for example), and so in the getPerson function a specific person would be retrieved based on its key (e.g. msg.sender) instead of its index number.

I’m not sure if you’re covered the section of the course on mappings yet, but you need to watch the videos on mappings to be able to understand my following example:

// Contract State (storage)
Person[] people;  // using an array
mapping(address => Person) people;  // or using a mapping

// addPerson function
Person memory newPerson = Person(30, "Bob");
people.push(newPerson);  // push to an array
people[msg.sender] = newPerson;  // or assign to a mapping

//getPerson function
Person memory person = people[_index]; // retrieve from an array
Person memory person = people[msg.sender]; // retrieve from a mapping
return(person.age, person.name);

As always, just let me know if you have any further questions.

Hi Jonathan,

I am really grateful for your help. I think I can follow much better the course now armed with the new informations provided by you. Yes, just started the video on " Introduction to Mapping". I will definitely seek your help if I do encounter any difficulty further down the road. Thanks a lot.

Eric Wong

1 Like

hi why does my remix doesnt work the same as the remix in the tutorial in loops do? i double checked the words and everything but it doesnt work the same way it do in the tutorial pls help.

Hi @josejalapeno,

Please post a copy of your code (the full file, including the pragma statement), so I can take a look and find out what the problem is.

Follow the instructions here, about how to correctly format your code when posting it in the forum. This will ensure it is clear, easier to read, and easier to debug :slight_smile:

oh sorry theres a very small thing I havent checked and now it totally works, thank you!

1 Like

hello Filip are there any shortcuts or keybinds worth noting? I’m looking for a way to place a semicolon at the end of the line without having to reach over and hit the end key

Hey @NoosphereLabs,

This isn’t something I have come across, but after a search I’ve found the following on Ethereum Stack Exchange, which mentions that Remix uses the Brace Editor which supports Emacs and Vim keybindings. I’ve never used these myself, so I can’t help you any further I’m afraid. But if you already have a lot of experience with keybindings, maybe this might give you some ideas about where to look next…

https://ethereum.stackexchange.com/questions/99312/remix-ide-shortcuts

@thecil Is this something you know about, or could help more with?

1 Like

I do not understand the issue to be honest XD, I have always programmed with the same keyboard layout (US layout), although this layout is another alternative for some programmers.

https://www.dvorak-keyboard.com/

Carlos Z

2 Likes

OK, thanks @thecil

I think @NoosphereLabs wants a single key binding that will both add a semicolon to the end of a line of code AND move the cursor to the beginning of the next line with the same indentation.

Personally, I’ve never had a problem with just hitting  shift + ;  and then hitting return separately … but I do appreciate that other programmers like to use shortcuts as much as possible.

2 Likes

Hi filip:

I am learning the Smart Contract Programming 101. The course is great,thank you !

Right now I have a small problem when I code in Remix about the “constructor”.

No matter what I typed to input , the decoded output is always the same —“Hello World!”.

Could you pls help to find where did i code wrong!

Thanks again!

Code attached.

pragma solidity 0.7.5;

contract HelloWorld{

string message;

constructor(string memory _message){
    message = _message;
}

function hello() public  returns(string memory){
   
    return message;
}

}

“but WHYYYYYY!!!” i heard john screaming…
ok no that was just me lol BUT i do have a real question (maybe it’s already been answered? the search function for the site --and page itself-- was more burdensome than helpful…); doesn’t it have everything to do with how the key-value pair is stored in memory??

i think this is the critical thing still missing in the videos… basically a key-value mapping IS an array (using string indices), just that under the hood the data is mapped to stored in computer memory differently such that access is much faster and scales more favorably with increases in data size…

no?

1 Like