【问题标题】:Is this the right way to capture a string by value?这是按值捕获字符串的正确方法吗?
【发布时间】:2020-05-09 10:16:42
【问题描述】:

我正在制作一个功能构建器来配置一个对象,例如:

struct Person
{
  name: String,
  position: String
}

构建器本身会保留一个盒装闭包列表,以便在需要构建对象时应用到它:

struct FunctionalBuilder<TSubject>
  where TSubject : Default
{
  actions: Vec<Box<dyn Fn(&mut TSubject) -> ()>>
}

impl<TSubject> FunctionalBuilder<TSubject>
  where TSubject : Default
{
  fn build(self) -> TSubject
  {
    let mut subj = TSubject::default();
    for action in self.actions
    {
      (*action)(&mut subj);
    }
    subj
  }
}

这个想法是可以聚合这个构建器,然后为一个对象定制它,比如Person。现在,假设我想要一个构建器方法called(),它接受一个名称并将名称的分配保存在闭包中。我实现如下:

impl PersonBuilder
{
  pub fn called(mut self, name: &str) -> PersonBuilder
  {
    let value = name.to_string();
    self.builder.actions.push(Box::new(move |x| {
      x.name = value.clone();
    }));
    self
  }
}

这是正确的做事方式吗?有没有更好的方法可以避免临时变量和clone() 调用?

完整的工作示例:

#[derive(Debug, Default)]
struct Person {
    name: String,
    position: String,
}

struct FunctionalBuilder<TSubject>
where
    TSubject: Default,
{
    actions: Vec<Box<dyn Fn(&mut TSubject) -> ()>>,
}

impl<TSubject> FunctionalBuilder<TSubject>
where
    TSubject: Default,
{
    fn build(self) -> TSubject {
        let mut subj = TSubject::default();
        for action in self.actions {
            (*action)(&mut subj);
        }
        subj
    }

    fn new() -> FunctionalBuilder<TSubject> {
        Self {
            actions: Vec::new(),
        }
    }
}

struct PersonBuilder {
    builder: FunctionalBuilder<Person>,
}

impl PersonBuilder {
    pub fn new() -> Self {
        PersonBuilder {
            builder: FunctionalBuilder::<Person>::new(),
        }
    }

    pub fn called(mut self, name: &str) -> PersonBuilder {
        let value = name.to_string();
        self.builder.actions.push(Box::new(move |x| {
            x.name = value;
        }));
        self
    }

    pub fn build(self) -> Person {
        self.builder.build()
    }
}

pub fn main() {
    let builder = PersonBuilder::new();
    let me = builder.called("Dmitri").build();
    println!("{:?}", me);
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=27eb6283836a478d5c68aa025aa4698d

【问题讨论】:

  • @Stargateur 添加了完成工作示例的链接
  • 那是说我不认为这种代码很生疏。
  • 另外,你可以只需要静态生命周期play.rust-lang.org/…

标签: rust closures


【解决方案1】:

您已经这样做了,value 归您的闭包所有,问题是您需要 Fn 特征。这意味着action(函数)需要能够被多次调用。这意味着需要克隆 value 以使其在闭包内保持有效。关闭不能放弃其所有权。

一种方法是使用FnOnce,这样可以删除clone,但这意味着构建器只能使用一次。为此,请使用actions: Vec&lt;Box&lt;dyn FnOnce(&amp;mut TSubject) -&gt; ()&gt;&gt;,action(&amp;mut subj);

更多:

【讨论】:

  • 虽然您的建议有效,但我不明白 为什么 它有效。 actionBox&lt;dyn FnOnce&gt;,如何只调用action(&amp;mut subj) 而不是通过*action 合法?
  • @DmitriNesteruk 您的原件也不需要 *action play.rust-lang.org/… 只是我需要摆脱它,因为 Box 不仅需要消耗内部的 FnOnce。这就是为什么我在更多中添加链接的原因,我也添加了博客公告。这是你在这里处理的一个相当高级的话题。 doc.rust-lang.org/1.34.0/std/boxed/…可以看到这个功能需要消耗盒子
猜你喜欢
  • 2017-12-10
  • 1970-01-01
  • 1970-01-01
  • 2021-10-12
  • 2015-01-11
  • 1970-01-01
  • 2013-09-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多