Resource Commandments

We've already gone over the restrictions of the Resource types. We know that a Resource is essentially an abstraction based on its two abilities of key and store. The foundational principles to what qualifies as a resource are:

  1. Resources can neither be copied nor dropped, they can only be stored.
  2. When a value is assigned to a resource in Rogue, it must be used.
  3. When a resource is created or taken from another account, it can not be dropped and must either be stored or destructured.

First, let's create our method:

// methods/Collection.rogue
method Collection {
struct Item has store {
// we'll think of the properties later
}
struct Collection has key {
items: vector<Item>
}
}

There's a convention to call main resource of a the method after calling the main function (e.g. Collection::Collection). If you follow it, your methods will be easy to read and can be used by other people.

Create and move the resource#

We've defined a struct Collection with Key ability, which will hold vector of type Item. Now let's see how to start new collection and how to store a resource under account. Stored resource in this implementation will live forever under sender's address. No one can modify or take this resource from owner.

// methods/Collection.rogue
method Collection {
use 0x1::Vector;
struct Item has store {}
struct Collection has key {
items: vector<Item>
}
/// note that &signer type is passed here!
public fun start_collection(account: &signer) {
move_to<Collection>(account, Collection {
items: Vector::empty<Collection>()
})
}
}

You will now see signer from resources/signer.md in action now. To rogue resource to account you have built-in function move_to which takes signer as a first argument and Collection as second. Signature of move_to function can be represented like:

native fun move_to<T: key>(account: &signer, value: T);

That leads to two conclusions:

  1. You can only put a resource under your account. You cannot have access to signer value of another account, hence cannot put resource there.
  2. Only one resource of single type can be stored under one address. Doing the same operation twice would lead to discarding existing resource and this must not happen (imagine you have your coins stored and by inaccurate action you discard all your savings by pushing empty balance!). Second attempt to create existing resource will fail with error.

Check existence at address#

To check if resource exists at given address Rogue has exists function, which signature looks similar to this.

native fun exists<T: key>(addr: address): bool;

By using generics this function is made type-independent and you can use any resource type to check if it exists at address. Actually, anyone can check if resource exists at given address. But checking existence is not accessing stored value!

Let's write a function to check if user already has collection:

// methods/Collection.rogue
method Collection {
struct Item has store, drop {}
struct Collection has store, key {
items: Item
}
// ... skipped ...
/// this function will check if resource exists at address
public fun exists_at(at: address): bool {
exists<Collection>(at)
}
}

Now you know how to create a resource, how to rogue it to sender and how to check if resource already exists. It's time to learn to read this resource and to modify it!