Using
directive
Binding methods to types with using
Methods can be bound to builtin types and any user-defined types like structs
using the using
syntax. This can be done either using libraries or free
standing functions.
using
with free standing functions
First, declare a function with one or more arguments. Once the function
is bound with using
, it can be called like a method.
function mask(uint v, uint bits) returns (uint) {
return v & ((1 << bits) - 1);
}
function odd(uint v) returns (bool) {
return (v & 1) != 0;
}
contract c {
using {mask, odd} for *;
uint v;
function set_v(uint n) public {
v = n.mask(16);
}
}
The using
declaration can be done on file scope. In this case, the type must
be specified in place of *
. The first argument must match the type that is
be used in the using
declaration.
If a user-defined type is used, the the global
keyword can be used. This
means the using
binding can be used in any file, even when the type is
imported.
struct User {
string name;
uint count;
}
function clear_count(User memory user) {
user.count = 0;
}
using {clear_count} for User global;
Now even when User
is imported, the clear_count() method can be used.
import {User} from "./user.sol";
contract c {
function foo(User memory user) public {
user.clear_count();
}
}
User defined Operators
The using
directive can be used to bind operators for user defined types
to functions. A binding can be set for the operators: ==
, !=
, >=
, >
, <=
, <
, ~
,
&
, |
, ^
, -
(both negate and subtract), +
, *
, /
, and %
.
First, declare a function with the correct prototype that implements the operator.
The function must be free standing: declared outside a contract.
The function must have
pure
mutability.All the parameters must be the same user type.
The number of arguments depends on which operator is implemented; binary operators require two and unary operators, one.
The function must return either
bool
for the comparison operators, or the same user type as the parameters for the other operators.
Then, bind the function to the operator using the syntax using {function-name as operator} for user-type global;
.
Operators can only be defined with global
set. Note that the -
operator is
used for two operators: subtract and negate. In order to bind the unary negate operator,
the function must have a single parameter. For the subtract operator, two parameters are required.
type Bitmap is int256;
function sub(Bitmap a, Bitmap b) pure returns (Bitmap) {
return Bitmap.wrap(Bitmap.unwrap(a) - Bitmap.unwrap(b));
}
function add(Bitmap a, Bitmap b) pure returns (Bitmap) {
return Bitmap.wrap(Bitmap.unwrap(a) + Bitmap.unwrap(b));
}
function neg(Bitmap a) pure returns (Bitmap) {
return Bitmap.wrap(-Bitmap.unwrap(a));
}
using {sub as -, neg as -, add as +} for Bitmap global;
function foo(Bitmap a, Bitmap b) {
Bitmap c = a + b;
// ...
}
using
with libraries
A library may be used for handling methods on a type. First, declare a library
with all the methods you want for a type, and then bind the library to the type
with using
.
struct User {
string name;
uint name;
}
library UserLibrary {
function clear_count(User user) internal {
user.count = 0;
}
function inc(User user) internal {
user.count++;
}
function dec(User user) internal {
require(user.count > 0);
user.count--;
}
}
using UserLibrary for User global;
Scope for using
The using
declaration may be scoped in various ways:
Globally by adding the
global
keyword. This means the methods are available in any file.Per file, by omitting the
global
keywordPer contract, by putting the
using
declaration in a contract definition
If the scope is per contract, then the type maybe be replaced with *
and
the type from the first argument of the function will be used.