1

So I'm having two problems with returning arrays from external functions:

Case One: arrays in getters

contract A {
  mapping(address => uint8[10]) public gameBoards;
}

// test.sol
function testGetGameBoard() {
  address myAddress;
  uint8[10] memory gameBoard = a.boards(address);
  Assert.equal(gameBoard[0], 0, 'uint8 arrays are initialized with 0');
  // TypeError: Member "equal" not unique after argument-dependent lookup in type(library Assert)

From this answer it looks like the compiler can't infer gameBoard[0] is a uint8? I'm not sure what's going on.

Case Two: arrays in structs

contract A {
  mapping(address => Game) public games;
  struct Game {
    address player;
    uint8[10] board;
  }
}

// test.sol
function testGetGame() {
  address myAddress;
  var(player, board) = a.games(myAddress);
  // TypeError: Not enough components (1) in value to assign all variables (2).

From what I understand, return values need to be known in size, which prevents you from returning dynamic arrays and strings. But a fixed array uint8[10] should be known in size, right? It's not clear to me why I'm getting both of these errors. Any explanation as to what's going on would be much appreciated.

3
  • I don't think either of these errors are about dynamic array return values. The first is about memory vs storage, and in any case your variable names don't match. In the second you're trying to read two variables from a function that only returns one. Oct 2, 2017 at 8:39
  • Hi @EdmundEdgar, thanks for the reply. The variables don't match because I'm trying to kludge my code into a format that's more readable. I've edited it to make the variables match :)
    – mzemel
    Oct 2, 2017 at 8:50
  • If you don't mind expanding on the second case, it looks like struct mappings return the destructured struct. Is that not true? For example struct Game { address playerOne; address playerTwo } allows me to destructure it as var(addrOne, addr2) = myContract.games(myAddress);
    – mzemel
    Oct 2, 2017 at 8:53

1 Answer 1

2

Yes, the size of uint8[10] is known. The problem is, there are two types of uint8[10]:

uint8[10] storage var1;
uint8[10] memory var2;

If you create a uint8[10] memory, the memory required for the array is dynamically allocated when the contract code starts running. It will be destroyed when the contract execution finishes.

A uint8[10] storage is more like a pointer to a uint8[10] which is stored in the blockchain as a contract member variable. A uint8[10] storage is not an array itself.

It is not possible to directly assign a uint8[10] memory to a uint8[10] storage pointer.

You should probably read the Solidity manual on this:

http://solidity.readthedocs.io/en/develop/types.html#data-location

I hope this helps.

5
  • Thanks Jesse for pointing me in the right direction. That is helpful. If you don't mind me asking, what's the correct move here? It looks ok to have a public mapping of address => uint8[10]. How should I consume it in another contract, e.g. the test? I'd like to get a particular value from the mapping and be able to access it by index. uint8[10] storage gameBoard = a.gameBoards(myAddress); will create a pointer to an array on the blockchain, right? So shouldn't I be able to access an element with []?
    – mzemel
    Oct 2, 2017 at 9:03
  • I try to assert its value to 0 with Assert.equal(gameBoard[0], 0, 'uint8 arrays are initialized with 0'); and get the Assert library type error I listed above.
    – mzemel
    Oct 2, 2017 at 9:06
  • @mzemel It's perfectly OK to have a mapping like that. You need to use square brackets to read from a mapping, just like reading from an array: a.gameBoards[myAddress]
    – Jesbus
    Oct 2, 2017 at 9:08
  • Hm, I was under the impression that public mappings use parens for accessing an element when its external to the contract declaring the mapping. For example, a.gameBoards[myAddress] results in TypeError: Indexed expression has to be a type, mapping or array (is function (address) external returns (uint8[10] memory)) I think without the parens it tries to evaluate a.gameBoards which is a function, externally, not a mapping.
    – mzemel
    Oct 2, 2017 at 9:23
  • @mzemel you are right. Per public parameter an 'automatic' view function is added. For arrays, this function will take the array index as input parameter. bun internally the mapping is handled as an array. Jul 18, 2019 at 12:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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