solidity/docs/contracts/using-for.rst

153 lines
4.8 KiB
ReStructuredText
Raw Normal View History

2019-01-07 17:08:00 +00:00
.. index:: ! using for, library
.. _using-for:
*********
Using For
*********
2021-10-11 08:16:52 +00:00
The directive ``using A for B;`` can be used to attach
functions (``A``) as member functions to any type (``B``).
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).
2021-10-11 08:16:52 +00:00
It is valid either at file level or inside a contract,
at contract level.
2019-01-07 17:08:00 +00:00
2021-10-11 08:16:52 +00:00
The first part, ``A``, can be one of:
- a list of file-level or library functions (``using {f, g, h, L.t} for uint;``) -
only those functions will be attached to the type.
- the name of a library (``using L for uint;``) -
all functions (both public and internal ones) of the library are attached to the type
At file level, the second part, ``B``, has to be an explicit type (without data location specifier).
Inside contracts, you can also use ``using L for *;``,
which has the effect that all functions of the library ``L``
are attached to *all* types.
If you specify a library, *all* functions in the library are attached,
2019-01-07 17:08:00 +00:00
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.
2021-10-11 08:16:52 +00:00
If you use a list of functions (``using {f, g, h, L.t} for uint;``),
then the type (``uint``) has to be implicitly convertible to the
first parameter of each of these functions. This check is
performed even if none of these functions are called.
2019-01-07 17:08:00 +00:00
The ``using A for B;`` directive is active only within the current
2021-10-11 08:16:52 +00:00
scope (either the contract or the current module/source unit),
including within all of its functions, and has no effect
outside of the contract or module in which it is used.
2019-01-07 17:08:00 +00:00
2021-11-16 16:01:09 +00:00
When the directive is used at file level and applied to a
user-defined type which was defined at file level in the same file,
the word ``global`` can be added at the end. This will have the
effect that the functions are attached to the type everywhere
the type is available (including other files), not only in the
scope of the using statement.
2019-01-07 17:08:00 +00:00
Let us rewrite the set example from the
2021-10-11 08:16:52 +00:00
:ref:`libraries` section in this way, using file-level functions
instead of library functions.
.. code-block:: solidity
2019-01-07 17:08:00 +00:00
// SPDX-License-Identifier: GPL-3.0
2021-10-11 08:16:52 +00:00
pragma solidity ^0.8.13;
2019-12-12 09:49:03 +00:00
struct Data { mapping(uint => bool) flags; }
2021-10-11 08:16:52 +00:00
// Now we attach functions to the type.
// The attached functions can be used throughout the rest of the module.
// If you import the module, you have to
// repeat the using directive there, for example as
// import "flags.sol" as Flags;
// using {Flags.insert, Flags.remove, Flags.contains}
// for Flags.Data;
using {insert, remove, contains} for Data;
function insert(Data storage self, uint value)
returns (bool)
{
if (self.flags[value])
return false; // already there
self.flags[value] = true;
return true;
}
2021-10-11 08:16:52 +00:00
function remove(Data storage self, uint value)
returns (bool)
{
if (!self.flags[value])
return false; // not there
self.flags[value] = false;
return true;
}
2021-10-11 08:16:52 +00:00
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
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-10-11 08:16:52 +00:00
It is also possible to extend built-in types in that way.
In this example, we will use a library.
.. code-block:: solidity
2019-01-07 17:08:00 +00:00
// SPDX-License-Identifier: GPL-3.0
2021-10-11 08:16:52 +00:00
pragma solidity ^0.8.13;
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
}
}
2021-10-11 08:16:52 +00:00
using Search for uint[];
2019-01-07 17:08:00 +00:00
contract C {
uint[] data;
function append(uint value) public {
data.push(value);
}
2022-02-16 02:59:49 +00:00
function replace(uint from, uint to) public {
2019-01-07 17:08:00 +00:00
// This performs the library function call
2022-02-16 02:59:49 +00:00
uint index = data.indexOf(from);
if (index == type(uint).max)
2022-02-16 02:59:49 +00:00
data.push(to);
2019-01-07 17:08:00 +00:00
else
2022-02-16 02:59:49 +00:00
data[index] = to;
2019-01-07 17:08:00 +00:00
}
}
2019-12-12 09:49:03 +00:00
Note that all external library calls are actual EVM function calls. This means that
if you pass memory or value types, a copy will be performed, even in case of the
2019-01-07 17:08:00 +00:00
``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.