考虑一个函数

fn clear_non_empty<T>(collection: &mut Vec<T>) {
    if !collection.is_empty() {
        collection.clear()
    }
}

它编译得很好,如果我试着把它推广到一些集合中

trait IsEmpty {
    fn is_empty(&self) -> bool;
}

trait Clear {
    fn clear(&mut self);
}

fn clear_non_empty<'a, Collection: Clear + IsEmpty>(collection: &'a mut Collection) {
    if !collection.is_empty() {
        collection.clear()
    }
}

我也没有问题.但是如果我把特征改成

trait IsEmpty {
    fn is_empty(self) -> bool;
}

trait Clear {
    fn clear(self);
}

fn clear_non_empty<'a, Collection>(collection: &'a mut Collection)
where
    &'a mut Collection: Clear,
    &'a Collection: IsEmpty,
{
    if !collection.is_empty() {
        collection.clear()
    }
}

我正在

error[E0502]: cannot borrow `*collection` as mutable because it is also borrowed as immutable
  --> src/lib.rs:15:9
   |
9  | fn clear_non_empty<'a, Collection>(collection: &'a mut Collection)
   |                    -- lifetime `'a` defined here
...
14 |     if !collection.is_empty() {
   |         ---------------------
   |         |
   |         immutable borrow occurs here
   |         argument requires that `*collection` is borrowed for `'a`
15 |         collection.clear()
   |         ^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

For more information about this error, try `rustc --explain E0502`.

我不明白,为什么特征的方法接受了self个引用,而特征的实现方法接受了self个引用却没有?

推荐答案

问题在于您声明要使用的生存期的方式:

fn clear_non_empty<'a, Collection>(collection: &'a mut Collection)
where
    &'a mut Collection: Clear,
    &'a Collection: IsEmpty,
{
    if !collection.is_empty() {
        collection.clear()
    }
}

你给了clear_non_emptylifetime parameter分.这意味着它是the caller可以 Select 的生命周期,并且它比整个clear_non_empty个函数调用都要长(因为在几乎所有情况下,这是函数能够在生命周期中生存的实际要求).但这不是你所需要的——你需要two个生命周期(一个用于拨打is_empty(),一个用于拨打clear()),do not overlap,个生命周期,这样它们就不会冲突.

生命周期 参数不能做到这一点,但实际上不需要生命周期 参数.clear_non_empty需要知道的是:"如果我borrow Collection,它会实现Clear吗?"事实上,Rust有一个语法(有点深奥的名字"高秩特征界限",或HRTB):

fn clear_non_empty<Collection>(collection: &mut Collection)
where
    for<'a> &'a mut Collection: Clear,
    for<'b> &'b Collection: IsEmpty,
{
    if !collection.is_empty() {
        collection.clear()
    }
}

(语法注释:for<'a>仅为以下绑定引入生存期,因此每个绑定could使用相同的名称;我写for<'b>只是为了强调它们是不同的.)

请注意,collection参数不再具有生存期-这也很重要,因为当我们调用collection.clear()时,我们没有向其传递the reference we got,我们传递的是一个生存期较短的隐式reborrow,因此它不会与is_empty的borrow 冲突.新的生命周期注释准确地反映了我们对collection的使用情况,而不是需要更长的生命周期.

当你写像trait Clear { fn clear(&mut self); }这样的特征时,你不必担心这一点,因为生命周期 量化——"对于任何生命周期 ‘a,我们都可以清除&'a mut Self"——隐含在Clear::clear中的function signature,可以明确地写为fn clear<'a>(&'a mut self);.通常,可以使用比特征实现者的任何约束都短的生命周期来调用特征中的函数.

Rust相关问答推荐

为什么幻影数据不能自动推断?

通过使用光标拖动角来绕其中心旋转矩形

是否可以在不切换到下一个位置的情况下获得迭代器值:

定义只有一些字段可以缺省的 struct

失真图像图形捕获Api

应为关联类型,找到类型参数

为什么BufReader实际上没有缓冲短寻道?

用于实现获取 struct 体 id 的特征规范

.在 Rust 模块标识符中

RUST 中的读写器锁定模式

如何从borrow 的异步代码运行阻塞代码?

更新 rust ndarray 中矩阵的一行

Option<&T> 如何实现复制

在 Rust 中实现资源消耗的安全包装器

在每个循环迭代中删除borrow

如何使返回 XMLError 的方法与 anyhow::Error 兼容?

我可以在不调用 .clone() 的情况下在类型转换期间重用 struct 字段吗?

Rustfmt 是否有明确类型的选项?

Rust 中的运行时插件

为什么当borrow 变量发生变化时,borrow 变量不会改变?