Struct
A struct is similar to an entity, but its instances exist in memory, not in a database.
Structs have the following characteristics:
- Attributes are immutable by default and only mutable when declared with a
mutable
keyword - An attribute might have a default value that's used if the attribute isn't specified during construction
- Structs get deleted from memory implicitly by a garbage collector
Example
struct user {
name: text;
address: text;
mutable balance: integer = 0;
}
Create structs
Employ the struct name as a function to create instances, specifying attribute values as arguments.
Example
val u = user(name = 'Bob', address = 'New York');
Struct-copy of entities and objects
You can use .to_struct()
to create a struct-copy of an entity or an object.
Example
entity user{
name;
address: text;
}
val u = user @ {.name == 'Bob'};
val s = u.to_struct(); // returns struct <user>
Create entities from structs
Conveniently create entities directly from struct<entity>
values.
Example
create user(s); // s is struct<user>
Same rules as for the create
expression apply: no need to specify attribute name if you can resolve it implicitly by
name or type:
val name = 'Bob';
val address = 'New York';
val u = user(name, address);
val u2 = user(address, name); // Order does not matter - same struct value is created.
Access struct attributes
You can use .
dot notation to access struct attributes.
Example
print(u.name, u.address);
Safe access for nullable structs
You can use the safe-access operator ?.
to read or modify attributes of a nullable struct.
Example
val u: user? = find_user('Bob');
u?.balance += 100; // no-op if 'u' is null
Struct<mutable T>
Struct<mutable T>
designates a struct where all attributes are mutable, enabling modifications after creation.
When working with functions that require a substantial number of arguments, it's recommended to use structs to enhance code organization and readability. This approach promotes better code maintainability and reduces the likelihood of errors arising from argument order confusion.
Consider the following example function that creates a project:
function create_project(
name: text,
description: text,
rules: text,
public_name: text
) {
...
}
While this function is straightforward, the potential for argument order mix-ups exists. To address this concern, we can introduce a dedicated struct to encapsulate the function's arguments:
struct create_project_arguments {
name: text,
description: text,
rules: text,
public_name: text
};
By defining this struct, we can now utilize it to create a type-safe and self-documenting function parameter:
function create_project(project_arguments: create_project_arguments) {
// ... Project creation logic
}
To invoke this function, we can instantiate the create_project_arguments
struct and assign values to its properties:
val arguments = create_project_arguments(
name: "test_project",
description: "my project description",
rules: "",
public_name: "my fancy test project"
);
create_project(arguments);
Struct<mutable T>
struct<mutable T>
represents a struct where all attributes are mutable, allowing for modification after creation.
Convert entities and objects to mutable structs
Use .to_mutable_struct()
to create a mutable struct representation:
val u = user @ { .name == 'Bob' };
val s = u.to_mutable_struct(); // will return a struct<mutable user>
Convert mutable and immutable structs
- Employ
.to_mutable()
to convert astruct<T>
to astruct<mutable T>
. - Use
.to_immutable()
to convert astruct<mutable T>
to astruct<T>
.
val s = u.to_struct();
val mut = s.to_mutable();
val imm = mut.to_immutable();
struct<operation>
The type struct<operation>
defines a struct that has the same attributes as a given operations parameters:
Example
operation add_user(name: text, rating: integer) {
// ...
}
query can_add_user(user: struct<add_user>) {
if (user.name == '') return false;
if (user.rating < 0) return false;
return true;
}
Functions available for all struct
types:
Function | Description |
---|---|
T.from_bytes(byte_array): T | Decode from a binary-encoded gtv . Same as T.from_gtv(gtv.from_bytes(x)) . |
T.from_gtv(gtv): T | Decode from a gtv . |
T.from_gtv_pretty(gtv): T | Decode from a pretty-encoded gtv . |
.to_bytes(): byte_array | Encode in binary format. Same as .to_gtv().to_bytes() . |
.to_gtv(): gtv | Convert to a gtv . |
.to_gtv_pretty(): gtv | Convert to a pretty gtv . |
.to_gtx_operation(): gtx_operation | Converts a struct<operation> to gtx_operation . |
.to_test_op(): rell.test.op | Converts a gtx_operation to a rell.test.op . |
Enhanced examples for struct usage
struct gtx_operation
Encapsulates individual operations within a transaction.
struct gtx_operation {
name: text;
args: list<gtv>;
}
struct gtx_transaction_body
Defines the core content of a transaction, excluding signatures.
struct gtx_transaction_body {
blockchain_rid: byte_array;
operations: list<gtx_operation>;
signers: list<gtv>;
}
struct gtx_transaction
Represents a complete transaction, including signatures.
struct gtx_transaction {
body: gtx_transaction_body;
signatures: list<gtv>;
}
struct rell.test.keypair
Facilitates test keypair generation and usage.
struct rell.test.keypair {
priv: byte_array; // private key, must be 32 bytes
pub: byte_array; // public key, must be 33 bytes
}