Solidity Arrays – How to Refer to a Struct within an Array

arrayssoliditystruct

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.

Best Answer

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.

Related Topic