1

I have a function that updates my Listing struct in DoubleMap Storage. How can I mutate the storage with given optional params?

pub fn update_listing(
    origin: OriginFor<T>,
    property_id: T::Hash,
    is_active: Option<bool>,
    content: Option<Content<T::MaxContentLength>>,
) -> DispatchResult {
    let who = ensure_signed(origin)?;
    ensure!(Listings::<T>::get(&property_id, &who).is_some(), Error::<T>::ListingNotFound);

    Listings::<T>::mutate(&property_id, &who, |o_listing| {
        let listing = o_listing.as_mut().unwrap();
        if let Some(is_active) = is_active {
            listing.is_active = is_active;
        }
        if let Some(content) = content {
            listing.content = content;
        }
        Some(listing)
    });

    Self::deposit_event(Event::ListingUpdated(property_id, who));
    Ok(())
}
error: lifetime may not live long enough
   --> pallets/listing/src/lib.rs:131:5
    |
123 |             Listings::<T>::mutate(&property_id, &who, |mut o_listing| {
    |                                                        -------------- return type of closure is std::option::Option<&'2 mut Listing<<T as frame_system::Config>::BlockNumber, <T as pallet::Config>::MaxContentLength>>
    |                                                        |
    |                                                        has type `&'1 mut std::option::Option<Listing<<T as frame_system::Config>::BlockNumber, <T as pallet::Config>::MaxContentLength>>`
...
131 |                 Some(listing)
    |                 ^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`

2 Answers 2

1
Listings::<T>::mutate(&property_id, &who, |listing| {
    // you don't need to use `as_mut` here. listing is already a `&mut _`
    if let Some(is_active) = is_active {
        listing.is_active = is_active;
    }
    if let Some(content) = content {
        listing.content = content;
    }
    // you can not return a local reference to outside, take the ownership first 
    // Some(listing.to_owned())
    // or
    Some(listing.clone())
});

Furthermore, you could use try_mutate or get/insert. While the listing is not changed, you could save one DB write cost.


Update for the OptionQuery:

Listings::<T>::mutate(&property_id, &who, |maybe_listing| {
    maybe_listing.as_mut().map(|listing| {
        if let Some(is_active) = is_active {
            listing.is_active = is_active;
        }
        if let Some(content) = content {
            listing.content = content;
        }

        Some(listing.clone())
    })
});
2
  • That works well for ValueQuery but since I use OptionQuery, listing is an Option. How could I use this in my case?
    – Burak
    Commented Aug 23, 2022 at 23:27
  • 1
    I've just updated the answer.
    – aurexav
    Commented Sep 22, 2022 at 5:57
0

Instead of using mutate, I get and edit the data and then insert it.

let mut listing =
    Listings::<T>::get(&property_id, &who).ok_or(Error::<T>::ListingNotFound)?;

if let Some(status) = status {
    listing.status = status;
}
if let Some(content) = content {
    listing.content = content;
}

Listings::<T>::insert(&property_id, &who, listing);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.