Cannot access elements of StorageMap -> Struct -> StorageVec

Description

When storing a Struct with a field that is a StorageVec as the value of a StorageMap in storage, we cannot access the elements of the storage vec through get. The resulting error indicates that the get function as used does not exist on the StorageVec type.

Below is a minimal reproduction of the issue and the toolchain version being used.

Fuelup Show

active toolchain

latest-aarch64-apple-darwin (default)
forc : 0.66.6
- forc-client
- forc-deploy : 0.66.6
- forc-run : 0.66.6
- forc-crypto : 0.66.6
- forc-debug : 0.66.6
- forc-doc : 0.66.6
- forc-fmt : 0.66.6
- forc-lsp : 0.66.6
- forc-tx : 0.66.6
- forc-wallet : 0.11.1
fuel-core : 0.40.0
fuel-core-keygen : 0.40.0

fuels versions

forc : 0.66.10
forc-wallet : 0.66.9

Minimal Reproduction

Code

contract;

use std::storage::storage_vec::*;
use std::storage::storage_map::*;
use std::hash::*;

abi MyContract {
    fn test_function() -> bool;
}

struct MyStruct {
  my_vec: StorageVec<u64>,
}

storage {
  my_map: StorageMap<u64, MyStruct> = StorageMap {},
}

impl MyContract for Contract {
    fn test_function() -> bool {
        let my_struct = MyStruct {
          my_vec: StorageVec {}
        };

        storage.my_map.insert(0, my_struct);

        let my_struct_key = storage.my_map.get(0);
        let my_storage_struct = my_struct_key.read();
        let my_vec = my_storage_struct.my_vec;
        let my_value = my_vec.get(0).unwrap().read();
    }
}

Error

         let my_storage_struct = my_struct_key.read();
30 |         let my_vec = my_storage_struct.my_vec;
31 |         let my_value = my_vec.get(0).unwrap().read();
   |                               ^^^ No method "get(StorageVec<u64>, numeric)" found for type "StorageVec<u64>".
32 |     }
33 | }
   |
1 Like

Hey @svimes :wave:

Currently, you can’t use a StorageVec as a field of a stored struct unfortunately. Planned improvements to storage should eventually remove this limitation.

To store a StorageVec within a struct, you will need to create another map, and store keys of that map in your struct, something like this:

contract;

use std::storage::storage_vec::*;
use std::storage::storage_map::*;
use std::hash::*;

abi MyContract {
    #[storage(read, write)]
    fn test_function() -> bool;
}

struct MyStruct {
    key: u64, // key into map2
}

storage {
  map1: StorageMap<u64, MyStruct> = StorageMap {},
  map2: StorageMap<u64, StorageVec<u64>> = StorageMap {},
}

impl MyContract for Contract {
    #[storage(read, write)]
    fn test_function() -> bool {
        // Insert into data
        storage.map1.insert(0, MyStruct { key: 0 });
        storage.map2.insert(0, StorageVec::<u64> {});
        storage.map2.get(0).push(1u64);

        // Get the data from the storage maps
        let key: u64 = 0;
        let my_storage_struct: MyStruct = storage.map1.get(key).read();
        let my_storage_vec: StorageKey<StorageVec<u64>> = storage.map2.get(my_storage_struct.key);

        // Read the vector values
        let my_storage_vec_value0: u64 = my_storage_vec.get(0).unwrap().read();
        let my_storage_vec_value1: u64 = my_storage_vec.get(1).unwrap().read();

        true
    }
}