【问题标题】:Why do I get "cannot move out of ... because it is borrowed" although no data is referenced?为什么我得到“不能搬出......因为它是借来的”虽然没有引用数据?
【发布时间】:2022-01-23 03:20:30
【问题描述】:
struct Population<'a> {
    dat: Vec<Genotype<'a>>,
}

impl<'a> Population<'a> {
    fn new(dat: Vec<Genotype<'a>>) -> Self {
        Population { dat }
    }

    fn select(&self) -> Genotype {
        self.dat.first().unwrap().clone()
    }
}

#[derive(Clone)]
struct Genotype<'a> {
    data: &'a str,
}

impl<'a> Genotype<'a> {
    fn new(data: &'a str) -> Self {
        Genotype { data }
    }
}

fn main() {
    let hello = "Hello World";
    let genotype = Genotype::new(hello);
    let mut population = Population::new(vec![genotype]);

    let other = population.select();
    drop(population);
    println!("{}", other.data);
}

Playground

编译器声称population不能被删除,因为它是在other中借用的:

error[E0505]: cannot move out of `population` because it is borrowed
  --> src/main.rs:32:10
   |
31 |     let other = population.select();
   |                 ------------------- borrow of `population` occurs here
32 |     drop(population);
   |          ^^^^^^^^^^ move out of `population` occurs here
33 |     println!("{}", other.data);
   |                    ---------- borrow later used here

我看不出这两个变量是如何相互关联的。我怀疑错误出在select 函数中,因为这显然是借用的来源。

我尝试为 select (fn select&lt;'b&gt;(&amp;self) -&gt; Genotype&lt;'b&gt;) 添加单独的生命周期,但失败了,因为我认为编译器假定 Population 实例的生命周期与返回的 Genotype 有某种联系,即使它涉及到内部&amp;str

我到底做错了什么?

【问题讨论】:

  • 这很难解释,play.rust-lang.org/…,最好的建议不要这样做,生命周期对 rust 初学者没有用处,只是不要在你的结构中使用参考。把事情简单化。如果您确实需要分享内容,请使用Rc
  • 我刚刚尝试过,编译器只是提示我应该将匿名生命周期添加到select 函数(fn select(&amp;self) -&gt; Genotype&lt;'_&gt;),但错误仍然存​​在。
  • @Stargateur 感谢您的帮助,这很有效。所以基本上这只是告诉编译器生命周期是专门链接到内部 &amp;str 而不是一些匿名生命周期,这有点正确吗?
  • @Shepmaster 啊,我现在明白你的意思了,谢谢!

标签: rust borrow-checker


【解决方案1】:

我怀疑错误出在 select 函数中,因为这显然是借用的来源。

是的。

编译器假定Population 实例的生命周期以某种方式与返回的Genotype 相关联

是的,这就是您的代码所说的情况。

即使它与内在的&amp;str有关

select 的函数签名的哪一部分表明了这一点?没有具体说什么,你依赖lifetime elision,所以你的函数签名是一样的:

fn select<'x>(&'x self) -> Genotype<'x>

换句话说,就是:“我正在返回一个Genotype,其中包含一个只要&amp;self 有效就保证有效的引用”。

相反,您可能想要:

fn select(&self) -> Genotype<'a>

换句话说,就是:“我正在返回一个 Genotype,其中包含一个引用,只要生命周期 'a 有效,该引用就保证有效”。


我强烈建议大家将#![deny(rust_2018_idioms)] 添加到每个 crate 的根目录中。这将导致像-&gt; Genotype 这样的代码出现编译器错误,提示程序员准确地考虑适合放置在那里的生命周期。

多年的经验表明,允许生命周期省略应用于其中包含生命周期的结构是一个糟糕的选择,而这种 lint 有助于改善这种情况。

【讨论】:

  • 也感谢添加 lint 的建议。我想我现在明白这有多重要了。
猜你喜欢
  • 1970-01-01
  • 2022-11-08
  • 2016-06-01
  • 2020-03-27
  • 1970-01-01
  • 2023-03-11
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多