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
|
2021-06-25 10:25:29 +00:00
|
|
|
:ref:`libraries` in this way:
|
|
|
|
|
|
|
|
.. code-block:: solidity
|
2019-01-07 17:08:00 +00:00
|
|
|
|
2020-05-13 15:45:58 +00:00
|
|
|
// SPDX-License-Identifier: GPL-3.0
|
2020-09-08 08:48:04 +00:00
|
|
|
pragma solidity >=0.6.0 <0.9.0;
|
2019-01-07 17:08:00 +00:00
|
|
|
|
2019-05-29 12:35:48 +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-05-29 12:35:48 +00:00
|
|
|
|
2019-12-12 09:49:03 +00:00
|
|
|
library Set {
|
2019-05-29 12:35:48 +00:00
|
|
|
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-05-29 12:35:48 +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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-25 10:25:29 +00:00
|
|
|
It is also possible to extend elementary types in that way:
|
|
|
|
|
|
|
|
.. code-block:: solidity
|
2019-01-07 17:08:00 +00:00
|
|
|
|
2020-05-13 15:45:58 +00:00
|
|
|
// SPDX-License-Identifier: GPL-3.0
|
2020-10-26 10:01:18 +00:00
|
|
|
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;
|
2020-10-26 10:01:18 +00:00
|
|
|
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);
|
2020-10-26 10:01:18 +00:00
|
|
|
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.
|