4

I am writing a smart contract that necessarily must have structs stored within an array. For example:

struct number {
uint number;
bool isCurrent;
}

number[] nums;

To add onto the array, I use the following command:

nums.push(number{number: 1, isCurrent: true});

My first question: is this a valid way of adding a new struct to the end of the array?

Second, to make my code more readable, I often do the following when I am trying to refer to a certain struct within the array (assume that nums[0] is what I pushed onto the array above):

var aNumberStruct = nums[0];
aNumberStruct.number = 2;

Does this change the value of "number" within the struct at nums[0]? Or does it simply make a copy of the struct, making it so I am not actually changing any data within the struct (at nums[0]) at all?

Thanks for any help. I really appreciate it.

1 Answer 1

5

Short Answer

Q1: Yes, it's a valid approach.

Q2: You better have a look at this. I can't describe it any better: http://vessenes.com/solidity-frustrations-references-and-mapping/

Best Answer

Exercise caution.

Detailed Answer

This initiated an interesting side conversation.

A colleague suggested this heuristic:

If the variable points to a specific slot in the storage, then it's not by reference. If it points to somewhere that needs further direction, then it is by reference.

I would take that to mean structs, mappings, arrays; anything with a [key] leads to a reference variable.

A test/demonstration shows how these references can have an impact on arrays; possibly updating something that wasn't supposed to be/expected to be updated. Obviously, updating storage with values that aren't supposed to be there can have non-trivial consequences.

The first example maps closely to your question code example. The second comes from some exploratory testing. Both are doing non-obvious updates to array storage through reference variables.

pragma solidity ^0.4.6;

contract Reference {

  struct NumberStruct {
    uint number;
    bool isCurrent;
  }

  NumberStruct[] nums;

  function Reference() {
    NumberStruct memory numberStruct;
    nums.push(numberStruct);
  }

  function setTwo() {
    var aNumberStruct = nums[0];
    // these references are writing to nums[]
    aNumberStruct.number = 2;
    aNumberStruct.isCurrent = true;
  }

  function getSlotZero() 
    constant
    returns(uint number, bool isCurrent) 
  {
    // you get 2, true after setTwo() and 0, false before setTwo()
    return(nums[0].number, nums[0].isCurrent)    ;
  }
}

// simplified example

contract Test {

  byte[20] v;

  function set() {
    var v1 = v;
    v1[0] = 1;
    // we have changed v[0]
  }

  function get() constant returns(byte value) {
    return(v[0]);
  }

}

Hope it helps.

5
  • Thank you very much. For Q2, thanks for sharing the article. It seems to be saying that for structs it maintains a reference to the variable - is this the case if I am using an array (in what you linked - a map is being used)? Essentially, because I am using an array instead of a map, do I still have a reference to a struct?
    – Nate Rush
    Commented Feb 14, 2017 at 20:35
  • I referred to the article so you would know this is weird area. I've updated my answer with details about how reference vars work with arrays. Commented Feb 14, 2017 at 22:56
  • This is fantastic. Thanks a ton for the help. I really appreciate it.
    – Nate Rush
    Commented Feb 14, 2017 at 23:39
  • I had some help. Wanting to not add to the confusion. Possible someone will chime in with a better or more formal explanation. Commented Feb 14, 2017 at 23:51
  • Also nominated reference variables as a topic for someone to tackle with a good explainer. meta.ethereum.stackexchange.com/questions/414/… Commented Feb 14, 2017 at 23:55

Not the answer you're looking for? Browse other questions tagged or ask your own question.