Reputation: 8224
I am attempting to model some program state as Mutables
from the futures-signals
library, whose value I want to set generically from a serde_json Value
identified by some string key.
For example, given I received some payload instructing me to update "my_int"
with a Value
, I want to be able to set the value of the Mutable
that is known as "my_int"
.
My idea was to have a map from identifiers like "my_int"
to a non-templated wrapper around a mutable's setter. It is important that said wrapper is non-templated, because otherwise I couldn't hold a collection of them in one map:
let my_int = Mutable::new(123);
let my_str = Mutable::new("asdf");
// ...
let setters = HashMap::from([
("my_int", /* magic wrapper around setter here somehow */),
("my_str", /* magic wrapper around setter here somehow */),
// ...
]);
let property_name = "my_int"; // constant for demo purposes
let value = Value::from(234); // constant for demo purposes
let setter = setters.get(property_name).unwrap();
(setter.deser_and_set)(value);
Right now said magic wrapper looks like this:
struct SetterWrapper<'a> {
name: &'static str,
deser_and_set: &'a dyn Fn(Value) -> Result<(), Error>,
// + some other unrelated fields
}
And I can create those inline, and it works:
let my_int_setter = SetterWrapper {
name: "my_int",
deser_and_set: &(|v: Value| {
my_int.set(serde_json::from_value(v)?);
Ok(())
}),
// + some other unrelated fields
};
But I have many mutables and don't want to repeat the above code for every one of them, so I attempted to put it into a function:
fn wrap_setter<'a, T>(name: &'static str, mutable: &'a Mutable<T>) -> SetterWrapper<'a>
where T: for<'de> Deserialize<'de>
{
let deser_and_set = |v: Value| {
mutable.set(serde_json::from_value::<T>(v)?);
Ok(())
};
SetterWrapper {
name,
deser_and_set: &deser_and_set,
}
}
which I intend to use like let my_int_setter = wrap_setter("my_int", &my_int);
, however I am encountering the following error:
error[E0515]: cannot return value referencing local variable `deser_and_set`
--> src\main.rs:66:5
|
66 | / SetterWrapper {
67 | | name,
68 | | deser_and_set: &deser_and_set,
| | -------------- `deser_and_set` is borrowed here
69 | | }
| |_____^ returns a value referencing data owned by the current function
The error itself makes sense to me: of course I can't return references to local variables, as those would dangle. But I believe conceptually I could solve the issue by somehow marking the closure in the function to have the same lifetime as the mutable, namely 'a
, but you cannot give variables lifetime annotations.
How can I solve this issue? Or is my approach already clumsy?
Upvotes: 0
Views: 161
Reputation: 1116
Probably the answer from @Joe_Jingyu is cleaner but I want to point out a second way you could take:
make SetterWrapper
a trait and implement it for Mutable
:
trait SetterWrapper {
fn deser_and_set(&self, v: Value) -> Result<(), Error>;
}
impl<T> SetterWrapper for Mutable<T>
where
T: for<'de> serde::de::Deserialize<'de>,
{
fn deser_and_set(&self, v: Value) -> Result<(), Error> {
self.set(serde_json::from_value::<T>(v)?);
Ok(())
}
}
Now you can create the HashMap with the trait objects and set the value:
let setters = HashMap::from([
("my_int", &my_int as &dyn SetterWrapper),
("my_str", &my_str),
]);
let property_name = "my_int"; // constant for demo purposes
let value = Value::from(234); // constant for demo purposes
let setter = setters.get(property_name).unwrap();
// now the call can be direct
setter.deser_and_set(value).unwrap();
Playground link (Note: I have build a simple Mutable myself, just to make the example work)
Upvotes: 1
Reputation: 1279
To work around the issue, one way I can think of is change the property deser_and_set
to a Box
from a reference. With that, the ownership of the Box can be moved out of the function. Give it a try.
struct SetterWrapper {
name: &'static str,
deser_and_set: Box<dyn Fn(Value) -> Result<(), Error>>,
// + some other unrelated fields
}
fn wrap_setter<T>(name: &'static str, mutable: &Mutable<T>) -> SetterWrapper
where T: for<'de> Deserialize<'de>
{
SetterWrapper {
name,
deser_and_set: Box::new(|v: Value| {
mutable.set(serde_json::from_value::<T>(v)?);
Ok(())
};),
}
}
Upvotes: 1