简体   繁体   English

生命周期问题:返回包含对本地闭包的引用的结构

[英]Lifetime issues: return struct containing reference to local closure

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.我正在尝试将一些程序状态Mutablesfutures-signals库中的Mutables ,我想从由某个字符串键标识的serde_json Value一般设置其值。

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" .例如,假设我收到了一些指示我用Value更新"my_int"负载,我希望能够设置称为"my_int"Mutable的值。

My idea was to have a map from identifiers like "my_int" to a non-templated wrapper around a mutable's setter.我的想法是有一个从像"my_int"这样的标识符到可变的 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);我打算使用像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.但我相信从概念上讲,我可以通过某种方式将函数中的闭包标记为与可变的生命周期相同,即'a ,但你不能给变量生命周期注释来解决这个问题。

How can I solve this issue?我该如何解决这个问题? Or is my approach already clumsy?还是我的方法已经很笨拙了?

To work around the issue, one way I can think of is change the property deser_and_set to a Box from a reference.为了解决这个问题,我能想到的一种方法是将属性deser_and_set从引用更改为Box With that, the ownership of the Box can be moved out of the function.这样,可以将 Box 的所有权移出函数。 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(())
        };),
    }
}

Probably the answer from @Joe_Jingyu is cleaner but I want to point out a second way you could take:可能来自@Joe_Jingyu 的答案更清晰,但我想指出您可以采取的第二种方法:

make SetterWrapper a trait and implement it for Mutable :使SetterWrapper成为一个 trait 并为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:现在您可以使用 trait 对象创建 HashMap 并设置值:

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) Playground 链接(注意:我自己构建了一个简单的 Mutable,只是为了让示例工作)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM