9

I have a contract with a mapping inside a struct

contract Voting {
 struct voter {
   address voterAddress;
   uint256 tokensBought;
   mapping (bytes32 => uint256) tokensUsed;
 }
 mapping (address => voter) public voterInfo;
}

I want a function that returns voter information given an address. I found out I can not have a function return a Struct. So, I tried writing a function which returns tokensBought and tokensUsed like below:

function voterDetails(address user) returns (uint256, mapping (bytes32 => uint256)) {
  return (voterInfo[user].tokensBought, voterInfo[user].tokensUsed);
}

When I try to compile this code, I get the following errors:

Error: Type is required to live outside storage.

Error: Internal type is not allowed for public or external functions

Looks like I can't return mapping type in a function. How do I write a function that returns all the voter information?

1 Answer 1

7

You need to break the interface down into smaller chunks. In practice, you'll want to extend the internal storage pattern a little, I think.

Iterable mapping comes to mind. You keep lists of keys to mappings in arrays. Something like this:

  struct VoterStruct {
    address voterAddress;
    uint256 tokensBought;
    mapping (bytes32 => uint256) tokensUsed;
    bytes32[] tokenIndex; // a list of mapping keys that exist for the voter
  }

  mapping (address => VoterStruct) public voterStructs;

Then you can expose functions that allow iterating over lists while the contract returns one set of values at a time. Very sketchy to inspire some ideas:

// client can discover how may tokens are in the list for one voter
function getVoterTokenCount(address voter) returns(uint tokenCount) {
    return voterStructs[voter].tokenIndex.length;}

// client can discover the tokenId for a voter in a given row
function getVoterTokenAtIndex(address voter, uint index) returns(bytes32 tokenId) {
    return voterStructs[voter].tokenIndex[index];}

// client can discover token used by voter and tokenId
function getVoterTokenUsed(address voter, bytes32 tokenId) returns(uint tokensUsed) {
    return voterStructs[voter].tokensUsed[tokenId];}

// append keys to the list as you go
function insertVoterToken(address voter, bytes32 tokenId) returns(bool success) {
    voterStructs[voter].tokenIndex.push(tokenId);
    return true;}

Hope I didn't flub the syntax. It might be possible to sketch out the contract a little more. Would need a general description of what it's going to do. When I use this pattern, I always add some rules to ensure there are no duplicate keys, and so on.

Anyway, possibly the hints will inspire some ideas.

Hope it helps.

6
  • 1
    Thanks, that helps. I am still curious, why can't the return type be a mapping? Is it just that solidity hasn't yet implemented it or is there a reason it can't be done at all? Feb 6, 2017 at 4:15
  • 2
    There's no way to enumerate the elements in a mapping. In order to know what to return, Solidity would need to keep track of all of the keys that have been used, which it does not do Feb 6, 2017 at 4:35
  • 1
    Can't return a mapping because it would be variable length if it could be enumerated at all, but it can't. We can track the keys ourselves and get a similar effect. Feb 6, 2017 at 5:10
  • I understand now, thank you @RobHitchens and @TjadenHess! Feb 6, 2017 at 16:37
  • 1
    @RFVenter. Above answer addresses the question as asked and also shows a way to keep track of keys in mappings. "checking all existing members in the list" is possibly misstated? It would be disastrous to iterate all keys in a list of n rows (anti-pattern). Mappings, on the other hand, provide no way to enumerate the keys. Really not sure what you meant. Jun 23, 2017 at 16:47

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.