What’s the proper way to copy a string
(or bytes
) from memory
to storage
? When copied directly into a state variable lvalue (or into a mapping), there’s no problem, but when assigning a string memory
rvalue into a string storage
lvalue, there’s a compiler error.
The Solidity documentation suggests that it ought to be possible to assign a memory
reference to a storage
reference and that the behavior is that the pointed-to value in memory is copied into the pointed-to location in storage. Why is it different when the lvalue is a reference instead of a state variable or mapping?
I suppose I could code this myself in Yul, but I’d prefer to avoid re-implementing the rules regarding the handling of short string
/bytes
in storage.
pragma solidity ^0.8.15; contract Works { string public thing; function setThing(string memory newValue) public { thing = newValue; } } contract AlsoWorks { mapping(bytes32 => string) public things; function setThing(bytes32 key, string memory newValue) public { things[key] = newValue; } } contract Fails { string public thing; function setThing(string memory newValue) public { string storage thingLocal = thing; thingLocal = newValue; } }
What I’m actually trying to do here is to store a string
at a slot that is impossible to accidentally clobber so that it is preserved across contract upgrades. A more concrete example:
pragma solidity ^0.8.15; abstract contract MagicSlot { uint256 internal constant _MAGIC_SLOT = 0xcafef00dcafef00dcafef00dcafef00dcafef00dcafef00dcafef00dcafef00d; function _magic() internal pure returns (string storage result) { assembly ("memory-safe") { result.slot := _MAGIC_SLOT } } function magic() external view returns (string memory) { return _magic(); } } contract Fails is MagicSlot { function setMagic(string memory newValue) public { // TypeError: Expression has to be an lvalue. // TypeError: Type string memory is not implicitly convertible to expected type string storage pointer. _magic() = newValue; } } contract AlsoFails is MagicSlot { function setMagic(string memory newValue) public { string storage magicRef = _magic(); // TypeError: Type string memory is not implicitly convertible to expected type string storage pointer. magicRef = newValue; } }