solidity/docs/contracts/using-for.rst

127 lines
3.6 KiB
ReStructuredText
Raw Normal View History

2019-01-07 17:08:00 +00:00
.. index:: ! using for, library
.. _using-for:
*********
Using For
*********
The directive ``using A for B;`` can be used to attach library
2019-12-12 09:49:03 +00:00
functions (from the library ``A``) to any type (``B``)
in the context of a contract.
2019-01-07 17:08:00 +00:00
These functions will receive the object they are called on
as their first parameter (like the ``self`` variable in Python).
The effect of ``using A for *;`` is that the functions from
the library ``A`` are attached to *any* type.
In both situations, *all* functions in the library are attached,
even those where the type of the first parameter does not
match the type of the object. The type is checked at the
point the function is called and function overload
resolution is performed.
The ``using A for B;`` directive is active only within the current
contract, including within all of its functions, and has no effect
outside of the contract in which it is used. The directive
may only be used inside a contract, not inside any of its functions.
Let us rewrite the set example from the
:ref:`libraries` in this way:
.. code-block:: solidity
2019-01-07 17:08:00 +00:00
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;
2019-01-07 17:08:00 +00:00
2019-01-07 17:08:00 +00:00
// This is the same code as before, just without comments
2019-12-12 09:49:03 +00:00
struct Data { mapping(uint => bool) flags; }
2019-12-12 09:49:03 +00:00
library Set {
function insert(Data storage self, uint value)
public
returns (bool)
{
if (self.flags[value])
return false; // already there
self.flags[value] = true;
return true;
}
function remove(Data storage self, uint value)
public
returns (bool)
{
if (!self.flags[value])
return false; // not there
self.flags[value] = false;
return true;
}
function contains(Data storage self, uint value)
public
view
returns (bool)
{
return self.flags[value];
}
2019-01-07 17:08:00 +00:00
}
2019-01-07 17:08:00 +00:00
contract C {
2019-12-12 09:49:03 +00:00
using Set for Data; // this is the crucial change
Data knownValues;
2019-01-07 17:08:00 +00:00
function register(uint value) public {
2019-12-12 09:49:03 +00:00
// Here, all variables of type Data have
2019-01-07 17:08:00 +00:00
// corresponding member functions.
// The following function call is identical to
// `Set.insert(knownValues, value)`
require(knownValues.insert(value));
}
}
It is also possible to extend elementary types in that way:
.. code-block:: solidity
2019-01-07 17:08:00 +00:00
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.8 <0.9.0;
2019-01-07 17:08:00 +00:00
library Search {
function indexOf(uint[] storage self, uint value)
public
view
returns (uint)
{
for (uint i = 0; i < self.length; i++)
if (self[i] == value) return i;
return type(uint).max;
2019-01-07 17:08:00 +00:00
}
}
contract C {
using Search for uint[];
uint[] data;
function append(uint value) public {
data.push(value);
}
function replace(uint _old, uint _new) public {
// This performs the library function call
uint index = data.indexOf(_old);
if (index == type(uint).max)
2019-01-07 17:08:00 +00:00
data.push(_new);
else
data[index] = _new;
}
}
2019-12-12 09:49:03 +00:00
Note that all external library calls are actual EVM function calls. This means that
2019-01-07 17:08:00 +00:00
if you pass memory or value types, a copy will be performed, even of the
``self`` variable. The only situation where no copy will be performed
2019-12-12 09:49:03 +00:00
is when storage reference variables are used or when internal library
functions are called.