1

Given a state variable defined like this:

struct Segment {
    uint256 foo;
    uint256 bar;
}

Segment[] public override segments;

Is it possible to define a getter in an interface?

I've tried this approach but it didn't work:

function segments() external view returns (Foo[] storage);

And changing it to memory does not work either. Is this doable at all?

2 Answers 2

1

Yes, it is possible to define a getter in an interface for an array of structs. Below is a complete example illustrating this.

Interface Definition

Firstly, you can define the interface with the segments function and specify Segment[] memory as its return type.

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

interface IStructContract {
    struct Segment {
        uint256 foo;
        uint256 bar;
    }

    // If you want to only read a single Segment at a given index then in the interface you have to explicitly destruct the Segment struct's fields in the return type,
    // however you won't have to explicitly define a getter for this i.e. it'll be implicitly defined at compile time
    function _segments(uint256 index) external view returns (uint256 foo, uint256 bar);

    // If you want to return the entire _segments array you can declare a function with the following signature in the interface,
    // however you'll have to explicitly define this function as shown in the contract below
    function segments() external view returns (Segment[] memory);
}

Contract Implementation

In your contract, you can then implement this interface. Make sure to override the segments function and ensure the return type matches with the interface, i.e. in this case Segment[] memory

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

contract StructContract is IStructContract {
    // You'll notice that the following variable is prepended with "_" as using "segments" gives a compile error indicating a name conflict with the "function segments()" below.
    // This is quite silly imo since solidity allows for function overloading and even if they had the same name the 2 functions would still have different signatures i.e.:
    // 1. function segments(uint256 index) external view;
    // 2. function segments() external view;
    Segment[] public override _segments;

    // Note: This function can be gas-intensive with increasingly large arrays
    function segments() external view override returns (Segment[] memory) {
        uint256 segmentsLength = _segments.length;
        Segment[] memory __segments = new Segment[](segmentsLength);

        for (uint256 i = 0; i < segmentsLength; i++) {
            __segments[i] = _segments[i];
        }
        return __segments;
    }

    // a simple setter for pushing a Segment onto the _segments storage variable
    function addSegment(uint _foo, uint _bar) external {
        Segment memory _segment = Segment({foo: _foo, bar: _bar});
        _segments.push(_segment);
    }

}

In this example, the contract StructContract correctly implements the IStructContract interface, and its segments function returns a memory array of Segment structs as expected.

1

Kamil Śliwak showed me how to do this in the Solidity Matrix server:

struct Segment {
    uint256 foo;
    uint256 bar;
}

interface I {
    function segments(uint256 index) external view returns (uint256, uint256);
}

Also copy-pasting his answer:

It is possible but it's not obvious.

First thing is that getter functions never return the whole array. They return items instead and you need an argument to represent the index.

The second is that due to historical reasons getters do not return structs directly (IIRC it's because they were introduced before structs had a well-defined representation in the ABI). Instead the struct gets unwrapped and you get its elements as separate return values.

2
  • Someone recommended me this answer a while back too. Though this returns values rather than the struct which was the original purpose. Commented Aug 1, 2023 at 5:54
  • you can still cast the struct when you implement or consume the interface. Commented Aug 1, 2023 at 10:01

Your Answer

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

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