solidity
solidity copied to clipboard
Allow functions inside structs
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
- Allow definition of functions inside a struct, e.g.
struct Set {...}. - Allow private members inside structs.
3. These are not included in the initializer
new Set(...). - Allow a custom
initializefunction. This would cancel out the autogeneratednew Set(...)function. - The functions can reference struct members as unqualified names and
this.qualified names. - Struct functions cannot be called statically (
Set.someFunction()). - 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.
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.
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.
This issue has been marked as stale due to inactivity for the last 90 days. It will be automatically closed in 7 days.
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
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.