【问题标题】:Conditionally clone when you need a reference需要参考时有条件地克隆
【发布时间】:2019-12-09 02:04:31
【问题描述】:

我有一个结构的引用。

我需要一个结构的引用。有时是一样的,但有时是克隆然后修改的。

现在,我愿意

if condition {
    let mut copy = original.clone();
    copy.select();
    do_big_thing(&copy);
} else {
    do_big_thing(original);
}

do_big_thing 接受其他参数,我不喜欢重复这一行。

有没有办法

  • 首先构建引用,可以是原始引用,也可以是对本地修改克隆的引用
  • 然后在do_big_thing中使用它(它不必活得更久)

?

这当然行不通,因为copy 的寿命不够长:

let mut reference = if condition {
    let mut copy = original.clone();
    copy.select();
    &copy
} else {
    original
};
do_big_thing(reference);

由于这个问题是关于找到一种更简洁、更简洁的方式来编写相同的东西(使用原始引用或克隆),我不能接受会在运行时增加开销或一些 unsafe 的解决方案。

playground

【问题讨论】:

  • 你只需要对你的代码做一点小小的改动:play.rust-lang.org/…
  • @SvenMarnach 这是谢普的回答。是的,这很简单,但我不知道这是允许的
  • 这里有两个很好的答案,由于编译器优化,两者都具有零成本。我建议未来的读者看看这两个答案。

标签: rust


【解决方案1】:

由于您不打算从您的函数中返回值(如Is it possible to return either a borrowed or owned type in Rust? 所示),您不需要使用Cow。相反,您可以在条件之外声明 copy 变量:

fn do_huge_thing_stack(original: &Big, a: usize, condition: bool) {
    let mut copy;

    let r = if condition {
        copy = original.clone();
        copy.select();
        &copy
    } else {
        original
    };

    do_big_thing(r, a);
}

编译器将确保仅在值有效时才进行引用。这会生成与Cow 版本相同的程序集,我认为the Cow version is more obvious,但它仍然是一个有用的技术。

另见:

【讨论】:

  • 但是……这是允许的吗?这确实是我想做的,但我不知道可以定义一个没有值的变量。
  • @DenysSéguret 并注意这种编码模式并不要求 copy 是可变的。我们只需要mut,因为select 需要它。
【解决方案2】:

您可以使用std::borrow::Cow 对借用或拥有的值进行抽象:

fn do_big_thing(b: Cow<Big>, a: usize) {
    println!("{}", a + b.c);
}

fn do_huge_thing(original: &Big, a: usize, condition: bool) {
    if condition {
        let mut copy = original.clone();
        copy.select();
        do_big_thing(Cow::Owned(copy), a);
    } else {
        do_big_thing(Cow::Borrowed(original), a);
    }
}

然后您可以将其重构为:

pub fn do_huge_thing_cow(original: &Big, a: usize, condition: bool) {
    let mut r = Cow::Borrowed(original);

    if condition {
        r.to_mut().select();
    }

    do_big_thing(&r, a);
}

作为you can see,这已被优化掉。

【讨论】:

  • 牛是一个枚举。我们现在有两个测试,而不是一个测试。还是编译器能够简化这个?
  • 如果有理由认为编译器可以删除第二个测试,请在答案中包含该信息部分。
  • @WesleyWiser 零成本抽象与优化代码无关,它是关于您无需为您手头完成的功能支付更多费用。如果你使用它的功能,Cow 会增加开销。然而在这里我们并没有真正使用它的特性而是它的逻辑,编译器能够看到并消除开销,但这不是零成本,只是优化,你不能指望编译器能够在每个奶牛。但是,当您像这里一样实际不使用 CoW 的功能时,您可以期待它
  • @DenysSéguret CoW 是 ZCA,但不是“免费”;)
  • ZCA 并不意味着“编译到零组装”,它意味着它不会比您自己从头开始编写它产生额外的开销。在 Rust 中,枚举、结构和函数通常是 ZCA。据我所知,Cow 是 ZCA。它的实现完全符合您的期望,仅此而已。 @Stargateur 你能提供一个不是 ZCA 的例子吗?
猜你喜欢
  • 2010-10-21
  • 2014-07-03
  • 1970-01-01
  • 1970-01-01
  • 2020-11-05
  • 2012-09-23
  • 1970-01-01
  • 1970-01-01
  • 2018-08-06
相关资源
最近更新 更多