1

I am receiving, the error below:

Type bytes32[] memory is not implicitly convertible to expected type bytes32[] storage pointer.

when unpacking what should be a bytes32 i.e. a in Problem struct (1).

//FileOne.sol
contract ExampleOne {

  struct Problem {
    bytes32 a; // (1)
    bytes32 b;
    bytes32[] c;
    uint d;
    }
  mapping (bytes32 => Problem) public problems;
}

//FileTwo.sol
contract IExampleOne {
  function problems(bytes32) public returns (bytes32, bytes32, bytes32[], uint);
  }

contract Example Two {

  IExampleOne exampleOne;

  constructor(address _exampleOne) {
    exampleOne = IExampleOne(_exampleOne);
  }

  function func(bytes32 name) {
    (bytes32 a, bytes32 b, bytes32[] c, uint d) = problems(name); //ERROR
    ...
  }
}
  • Your coding is not so easy to read. Why do you use the same name for different objects in different files? May be it could better rewrite it in a more readable manner and reopen the question. Anyway, generally speaking, a cast can solve the formal problem, but who can say that it results in what you expect to? – Rick Park Nov 15 '18 at 22:12
1

Good news. It compiles with a few tweaks.

Bad news. This is hard on gas and depending on the length of the arrays being passed around it may be too expensive to be practical - standard caution whenever arrays are involved in the interface.

As an alternative, you could create getters for the scalar values (return (p.a, p.b, p.d)) and individual rows in the array (return p.c[row];). Doing so will ensure the individual invocations will cost the same and conserve gas.

For your code to compile, the trick is to cast c[] as a memory pointer. That corresponds to the data received from the other contract via the ABI (i.e. not from storage). By default, it was a storage pointer.

//FileOne.sol
contract ExampleOne {

  struct Problem {
    bytes32 a; // (1)
    bytes32 b;
    bytes32[] c;
    uint d;
    }
  mapping (bytes32 => Problem) public problems;
}

//FileTwo.sol
contract IExampleOne {
  function problems(bytes32) public returns (bytes32, bytes32, bytes32[], uint);
  }

contract ExampleTwo {

  IExampleOne exampleOne;

  constructor(address _exampleOne) {
    exampleOne = IExampleOne(_exampleOne);
  }

  function func(bytes32 name) {
    (bytes32 a, bytes32 b, bytes32[] memory c, uint d) = exampleOne.problems(name); // NO ERROR
    //...
  }
}

In case the comments about scalability are of interest, the concern is it will take increasing amounts of gas on both sides to pack and unpack larger and larger lists. Your ExampleOne can offer the potential to read it at any scale using many tiny calls instead of one big one.

function getProblemMeta(bytes32 problemId) public view returns(bytes32, bytes32, uint) {
  p = problems[problemId];
  return (p.a, p.b, p.d);
}

function getProblemCAtIndex(bytes32 problemId, uint index) public view returns(bytes32) {
  return problems[problemId].c[index];
}

Hope it helps.

| improve this answer | |
  • Hi Rob, thank you for this thorough answer. Casting to memory solves this issue I was having. Scalability is definitely of interest, glad you noticed my current implementation is poor in that regard. I'll try to incorporate those smaller calls where I can! – isaacsultan Nov 16 '18 at 10:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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