在 Rust 的元组中传递多个引用

Pass multiple references in a tuple in Rust

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

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 的实现是没有意义的,所以我更愿意使用关联类型。

感谢您的宝贵时间:-)

I tried to have Struct::Value be of type (&i64, &String), but then the borrow checker complains about needing a lifetime for those references, which I'd like to avoid if possible.

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

In that case, I can have Struct implement Trait<(&i64, &String)> without the lifetime issue I had with an associated type.

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

It works, but in my project it never makes sense for Struct to have more than one implementation of Trait, so I'd much prefer to use an associated type.

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

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