Intro to Generics
Generics are abstract stand-ins for concrete types or other properties. They work similarly to generics in Rust, and can be used to allow greater flexibility and avoid logic duplication while writing Sui Move code.
Generics are a key concept in Sui Move, and it's important to understand and have an intuition for how they work, so take your time with this section and understand every part fully.
Generics Usage
Using Generics in Structs
Let's look at a basic example of how to use generics to create a container Box
that can hold any type in Sui Move.
First, without generics, we can define a Box
that holds a u64
type as the following:
module generics::storage {
struct Box {
value: u64
}
}
However, this type will only be able to hold a value of type u64
. To make our Box
able to hold any generic type, we will need to use generics. The code would be modified as follows:
module generics::storage {
struct Box<T> {
value: T
}
}
Ability Constraints
We can add conditions to enforce that the type passed into the generic must have certain abilities. The syntax looks like the following:
module generics::storage {
// T must be copyable and droppable
struct Box<T: store + drop> has key, store {
value: T
}
}
💡It's important to note here that the inner type T
in the above example must meet certain ability constraints due to the outer container type. In this example, T
must have store
, as Box
has store
and key
. However, T
can also have abilities that the container doesn't have, such as drop
in this example.
The intuition is that if the container is allowed to contain a type that does not follow the same rules that it does, the container would violate its own ability. How can a box be storeable if its content isn't also storeable?
We will see in the next section that there is a way to get around this rule in certain cases using a special keyword, called phantom
.
💡See the generics project under example_projects
for some examples of generic types.
Using Generics in Functions
To write a function that returns an instance of Box
that can accept a parameter of any type for the value
field, we also have to use generics in the function definition. The function can be defined as the following:
public fun create_box<T>(value: T): Box<T> {
Box<T> { value }
}
If we want to restrict the function to only accept a specific type for value
, we simply specify that type in the function signature as follows:
public fun create_box(value: u64): Box<u64> {
Box<u64>{ value }
}
This will only accept inputs of the type u64
for the create_box
method, while still using the same generic Box
struct.
Calling Functions with Generics
To call a function with a signature that contains generics, we must specify the type in angle brackets, as in the following syntax:
// value will be of type storage::Box<bool>
let bool_box = storage::create_box<bool>(true);
// value will be of the type storage::Box<u64>
let u64_box = storage::create_box<u64>(1000000);
Calling Functions with Generics using Sui CLI
To call a function with generics in its signature from the Sui CLI, you must define the argument's type using the flag --type-args
.
The following is an example that calls the create_box
function to create a box that contains a coin of the type 0x2::sui::SUI
:
sui client call --package 0xbff5d0f3202e3b19e622b13819f269ae9aad904436cc557bace358266938e2ec --module generics --function create_box --args $OBJECT_ID --type-args 0x2::sui::SUI --gas-budget 10000000
Note: tạo 1 object simple_box then put it into a box
Advanced Generics Syntax
For more advanced syntax involving the use of generics in Sui Move, such as multiple generic types, please refer to the excellent section on generics in the Move Book.
But for our current lesson on fungible tokens, you already know enough about how generics work to proceed.
Here is example code
Referent
- Part 6: Events
Top comments (0)