Problem with using rent_mut
Closed this issue · 4 comments
Hi,
I'm currently trying to use rental for help building an interface of fluent-bundle (https://github.com/projectfluent/fluent-rs/blob/master/fluent-bundle/src/bundle.rs) so that the lifetime won't be exposed and able to compile to Web Assembly, Now I have the rental and part of the interface like this:
rental! {
mod my_rentals {
use super::*;
#[rental(covariant)]
pub struct LocalizationInterface {
resources: Box<FrozenMap<String, Box<FluentResource>>>,
bundles: HashMap<String, FluentBundle<'resources>>,
}
}
}
#[wasm_bindgen]
pub struct LocalizationInterface(my_rentals::LocalizationInterface);
#[wasm_bindgen]
impl LocalizationInterface {
pub fn new() -> Self {
Self(my_rentals::LocalizationInterface::new(
Box::new(FrozenMap::new()),
|_resources| { HashMap::new() }
))
}
pub fn add_bundle(&mut self, bundle_id: String, locales: String) {
let bundle = FluentBundle::new(&[locales]);
//let fields = self.0.all();
//fields.bundles.insert(bundle_id, bundle);
self.0.rent_mut(|bundles| bundles.insert(bundle_id, bundle));
}
}
I was trying to use fields.bundles.insert(bundle_id, bundle)
to insert a bundle to the bundles, but it gave me this error:
error[E0596]: cannot borrow `*fields.bundles` as mutable, as it is behind a `&` reference
--> src/lib.rs:970:9
|
970 | fields.bundles.insert(bundle_id, bundle);
| ^^^^^^^^^^^^^^ cannot borrow as mutable
For the resources, this problem can be avoided by using FrozenMap. But for bundles, it cannot use the FrozenMap since it has a lifetime in it. So I tried to use rent_mut to mutably borrow the bundles and insert a bundle in it, but I think I didn’t use it correctly and get this compile error:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/lib.rs:971:35
|
971 | self.0.rent_mut(|bundles| bundles.insert(bundle_id, bundle));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #3 defined on the body at 971:25...
--> src/lib.rs:971:25
|
971 | self.0.rent_mut(|bundles| bundles.insert(bundle_id, bundle));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...so that the expression is assignable:
expected std::option::Option<FluentBundle<'_>>
found std::option::Option<FluentBundle<'_>>
note: but, the lifetime must be valid for the method call at 971:9...
--> src/lib.rs:971:9
|
971 | self.0.rent_mut(|bundles| bundles.insert(bundle_id, bundle));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so type `std::option::Option<FluentBundle<'_>>` of expression is valid during the expression
--> src/lib.rs:971:9
|
971 | self.0.rent_mut(|bundles| bundles.insert(bundle_id, bundle));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Can I get some help about how to use rent_mut correctly? (Or is there some other way to make the insert works?)
/cc @zbraniecki
My first hunch is that the problem is HashMap::insert
, which returns the old value in an Option
. Since this value has a lifetime param for the type you're using, the closure is trying to return that value using an internal rental lifetime, which is illegal. Try adding a semicolon or otherwise discarding the return value of insert
to prevent it from leaving the closure.
Sorry, I think I understand what you said, but I'm not very familiar with rust. For "adding a semicolon", I'm not sure where should I put the semicolon in, I've tried several places to put it, but none of them compiled. And for "discarding the return value of insert
", do you mean something like self.0.rent_mut(|bundles| bundles.insert(bundle_id, bundle).unwrap());
or self.0.rent_mut(|bundles| bundles.entry(bundle_id).or_insert_with(|| bundle));
?
Sorry, I should have been more specific. I mean something like this:
self.0.rent_mut(|bundles| { bundles.insert(bundle_id, bundle); })
A closure with a single expression will try to return the value of that expression, which won't work in this case since that value has a rental lifetime attached. A semicolon in rust suppresses the return value of an expression, but we need to put the expression in braces first for proper syntax.
Thank you, now I actually understand how it works! I really appreciate your time and assistance!