Consuming vector of resources ( vector<T: resource> )

Hello! I’ve met a problem while trying to consume (after move_from op) a vector of resources. Although I can think of a way of avoiding this case, I got curious - is there a solution to this problem at all?

Guesses:

  1. vector has dynamic length and is stored in a resource, hence it is not possible to statically check whether vector has elements or not.
  2. destructuring every element of vector may seem good but compiler can’t ‘read/understand’ loops :confused:
module M {

    use 0x1::Vector;
    use 0x1::Signer;

    resource struct S {
        prop: u8
    }

    resource struct T {
        storage: vector<S>
    }

    public fun init(account: &signer) {
        let storage = Vector::empty<u8>();

        Vector::push_back(&mut vec, S { prop: 1 });
        Vector::push_back(&mut vec, S { prop: 2 });

        move_to(account, T { storage });
    }

    public fun destroy(account: &signer) acquires T {
        let T { storage: _ } = move_from<T>(Signer::address_of(account));
        //               ^ Cannot ignore resource values. The value must be used
    }
}

This code didn’t pass compiler:

public fun destroy(account: &signer) acquires T {
    let T { storage } = move_from<T>(Signer::address_of(account));
    let len = Vector::length<S>(&storage);
    let i   = 0;

    while (i < len) {
        let S { prop: _ } = Vector::pop_back(&mut storage);
        i = i + 1;
    }
}

Am I missing something? How can I consume vector<T: resource>?

Hi Damir,
Adding Vector::destroy_empty(storage) after your loop should do the trick. This makes the compiler/bytecode verifier, but will fail at runtime if storage is not actually empty. The runtime check is somewhat unfortunate, but (as you mention in (1)), there’s not a good static solution for this. A similar situation arises with Libra::destroy_zero<T>(c), which lets you destroy a zero-valued coin c, but fails at runtime if c's value is nonzero.

1 Like

Thank you, Sam! I was wondering what this method does a month ago when I went through the Vector module and could never think of it in this case.

So final solution is this:

public fun destroy(account: &signer) acquires T {
    let T { storage } = move_from<T>(Signer::address_of(account));

    while (Vector::length<S>(&storage) > 0) {
        let S { prop: _ } = Vector::pop_back(&mut storage);
    };

    Vector::destroy_empty(storage);
}

Looks good to me and compiles without errors. Nice!

1 Like