【问题标题】:Pass multiple references in a tuple in Rust在 Rust 的元组中传递多个引用
【发布时间】:2021-02-07 13:32:09
【问题描述】:

我有一个具有某种关联类型的特征,它的一些函数将该类型的引用作为输入:

trait Trait {
    type Value;

    fn do_something(&self, value: &Self::Value);
}

在一个实现此特征的结构中,Value 是一个元组,因为我需要将多个值传递给do_something

struct Struct {}

impl Trait for Struct {
    type Value = (i64, String);

    fn do_something(&self, (a, b): &(i64, String)) {
        println!("{} {}", a, b);
    }
}

但是,当我在实践中使用Struct 时,我碰壁了。要么我这样做:

fn main() {
    let a = 10;
    let b = "foo".to_owned();
    let s = Struct {};

    s.do_something(&(a, b));  // This works...
    println!("{}", b);  // ...but the borrow checker complains here
}

但是我失去了元组中任何非Copy 类型的所有权。或者,我可以这样做:

fn main() {
    let a = 10;
    let b = "foo".to_owned();
    let s = Struct {};

    let t = (a, b);
    s.do_something(&t);
    let (a, b) = t;
    println!("{}", b);
}

可行,但语法非常繁重。

有没有人知道如何以更简洁的方式完成我想要的事情?我试图让Struct::Value 的类型为(&i64, &String),但是借用检查器抱怨这些引用需要一个生命周期,如果可能的话我想避免这种情况。

我尝试的另一种选择是使用类型参数(即Trait<Value>)而不是关联类型。在这种情况下,我可以让Struct 实现Trait<(&i64, &String)>,而不会出现关联类型的生命周期问题。它可以工作,但在我的项目中,Struct 拥有多个 Trait 的实现是没有意义的,所以我更喜欢使用关联类型。

感谢您的宝贵时间 :-)

【问题讨论】:

  • 在你上面的第一个例子中,你不能使用b.clone() 来获取字符串的副本吗?它不太理想,但可能对您的情况有用!

标签: rust reference tuples lifetime borrow-checker


【解决方案1】:

我试图让Struct::Value 成为(&i64, &String) 类型,但借用检查器抱怨这些引用需要生命周期,如果可能的话我想避免这种情况。

无法避免生命周期。所有引用都有生命周期,其中一些是编译器可以为你推断的,而其中一些你必须自己显式注释。注意:编译器并不总是正确的,所以有时你必须明确地注释生命周期,尽管编译器的推论。

在这种情况下,我可以让 Struct 实现 Trait<(&i64, &String)> 而不会出现关联类型的生命周期问题。

是的,因为编译器为这些引用推断出的生命周期恰好有效,但您不必害怕在需要时自己显式注释它们。

它有效,但在我的项目中,Struct 拥有多个 Trait 的实现是没有意义的,所以我更喜欢使用关联类型。

好吧,那么让我们使用关联类型。这是包含所有必要生命周期注释的代码:

trait Trait<'a> {
    type Value;

    fn do_something(&self, value: Self::Value);
}

struct Struct {}

impl<'a> Trait<'a> for Struct {
    type Value = (&'a i64, &'a String);

    fn do_something(&self, (a, b): Self::Value) {
        println!("{} {}", a, b);
    }
}

fn main() {
    let a = 10;
    let b = "foo".to_owned();
    let s = Struct {};

    s.do_something((&a, &b)); // compiles
    println!("{}", b); // compiles
}

playground

【讨论】:

  • 非常感谢!我觉得我仍然觉得终身注释有点可怕,尤其是它们往往会到处蔓延,所以我总是不愿意使用它们……但我没想到总是 i> 反正一辈子,只是有时编译器会自动为我添加它。
  • 是的!我强烈推荐阅读Common Rust Lifetime Misconceptions,我想你会发现它有助于揭开生命的神秘面纱。免责声明:我写的。
  • 这真的很广泛:0 谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-07
  • 2018-03-29
  • 2018-02-23
  • 2017-08-14
  • 1970-01-01
  • 2015-07-08
相关资源
最近更新 更多