solidity icon indicating copy to clipboard operation
solidity copied to clipboard

Allow functions inside structs

Open fulldecent opened this issue 4 years ago • 2 comments

Abstract

Putting functions and private members inside a struct creates first-class data structures.

Motivation

People want data structures. There are many inconsistent ways to do this. And they are not semantic. A struct with functions is semantic, everybody understands what it is, and clearly articulates what you are doing.

Currently people use something like:

    using BitMaps for BitMaps.BitMap;

    BitMaps.BitMap private _bitmap;

This does not allow necessary encapsulation or access control on _bitmap. You are directly able to touch the members inside which should be private.

Specification

  1. Allow definition of functions inside a struct, e.g. struct Set {...}.
  2. Allow private members inside structs. 3. These are not included in the initializer new Set(...).
  3. Allow a custom initialize function. This would cancel out the autogenerated new Set(...) function.
  4. The functions can reference struct members as unqualified names and this. qualified names.
  5. Struct functions cannot be called statically (Set.someFunction()).
  6. The only way to call the functions is on the variable like someSet.add(newElement).

Example:

// Data structure for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential.
struct BitMap {
    mapping(uint256 => uint256) private _data;

    /// @dev Returns whether the bit at `index` is set.
    function get(uint256 index) internal view returns (bool) {
        uint256 bucket = index / 256;
        uint256 mask = 1 << (index % 256);
        return _data[bucket] & mask != 0;
    }

    /// @dev Sets the bit at `index` to the boolean `value`.
    function setTo(uint256 index, bool value) internal {
        if (value) {
            set(index);
        } else {
            unset(index);
        }
    }

    /// @dev Sets the bit at `index`.
    function set(uint256 index) internal {
        uint256 bucket = index / 256;
        uint256 mask = 1 << (index % 256);
        _data[bucket] |= mask;
    }

    /// @dev Unsets the bit at `index`.
    function unset(uint256 index) internal {
        uint256 bucket = index / 256;
        uint256 mask = 1 << (index % 256);
        _data[bucket] &= ~mask;
    }
}

Usage

contract BitMapMock {
    Bitmap private _bitmap;

    function get(uint256 index) public view returns (bool) {
        return _bitmap.get(index);
    }

    function setTo(uint256 index, bool value) public {
        _bitmap.setTo(index, value);
    }

    function set(uint256 index) public {
        _bitmap.set(index);
    }

    function unset(uint256 index) public {
        _bitmap.unset(index);
    }
}

Backwards Compatibility

None

Notes

In the future we might consider static functions such as Set.createRangeFromOneTo(5) where those functions have "protected" access to same-class members.

This greatly reduces the use case for Library.

fulldecent avatar Jul 28 '21 18:07 fulldecent

For functions inside structs, it's not clear what would be the storage location of the struct. Not sure if that's a good idea.

For example:

struct S {
   bool b;
   function set() internal {
     x = 1
   }
}

The above function on the struct is only valid for memory and storage and not calldata.

hrkrshnn avatar Aug 02 '21 10:08 hrkrshnn

We are currently going the route of declaring functions at file level and then attaching them to structs. The encapsulation aspect, though, could still be considered.

chriseth avatar Aug 02 '21 10:08 chriseth

This issue has been marked as stale due to inactivity for the last 90 days. It will be automatically closed in 7 days.

github-actions[bot] avatar Mar 20 '23 12:03 github-actions[bot]

Regarding storage location, that should be entirely hidden from the developer. That is an implementation detail of Solidity.

@chriseth please advise if anything here left to discuss or we can close

fulldecent avatar Mar 20 '23 14:03 fulldecent

The mechanism of choice for this is free-functions grouped together in modules that act on user-defined types (which will be extended to all types beyond value types eventually for this purpose, once we started addressing the question of data locations). So this feature request can be considered as superseded by a different design direction and be closed.

ekpyron avatar Mar 20 '23 15:03 ekpyron