如何为涉及对中间局部变量的引用的闭包指定生存期限?

Jo *_*tes 5 generics reference lifetime rust

我正在尝试在Rust中编写类似以下的函数:

fn double_and_square<'a, T>(x: &'a T) -> /* whatever the output type of `&t * &t` is */ {
    let t = x + x;
    &t * &t
}
Run Code Online (Sandbox Code Playgroud)

我希望它适用于T非类型的类型Copy.我需要指定不仅&'a T器具Add(容易),也表明其与局部变量的寿命输出类型的参考t工具Mul.

尝试#1(没有为中间类型指定生命周期):

fn double_and_square<'a, T>(x: &'a T) -> <&<&'a T as Add>::Output as Mul>::Output
where
    &'a T: Add,
    &<&'a T as Add>::Output: Mul,
{
    let t = x + x;
    &t * &t
}
Run Code Online (Sandbox Code Playgroud)

导致以下编译器错误:

error[E0106]: missing lifetime specifier
 --> src/main.rs:6:5
  |
6 |     &<&'a T as Add>::Output: Mul,
  |     ^ expected lifetime parameter
Run Code Online (Sandbox Code Playgroud)

尝试#2(好吧,我将添加一个生命周期说明符):

fn double_and_square<'a, 'b, T>(x: &'a T) -> <&'b <&'a T as Add>::Output as Mul>::Output
where
    &'a T: Add,
    &'b <&'a T as Add>::Output: Mul,
{
    let t = x + x;
    &t * &t
}
Run Code Online (Sandbox Code Playgroud)

导致以下编译器错误:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
 --> src/main.rs:8:13
  |
8 |     let t = x + x;
  |             ^
  |
note: first, the lifetime cannot outlive the lifetime 'a as defined on the function body at 3:1...
 --> src/main.rs:3:1
  |
3 | / fn double_and_square<'a, 'b, T>(x: &'a T) -> <&'b <&'a T as Add>::Output as Mul>::Output
4 | | where
5 | |     &'a T: Add,
6 | |     &'b <&'a T as Add>::Output: Mul,
... |
9 | |     &t * &t
10| | }
  | |_^
note: ...so that expression is assignable (expected &T, found &'a T)
 --> src/main.rs:8:13
  |
8 |     let t = x + x;
  |             ^
note: but, the lifetime must be valid for the lifetime 'b as defined on the function body at 3:1...
 --> src/main.rs:3:1
  |
3 | / fn double_and_square<'a, 'b, T>(x: &'a T) -> <&'b <&'a T as Add>::Output as Mul>::Output
4 | | where
5 | |     &'a T: Add,
6 | |     &'b <&'a T as Add>::Output: Mul,
... |
9 | |     &t * &t
10| | }
  | |_^
note: ...so that the type `<&T as std::ops::Add<&'a T>>::Output` is not borrowed for too long
 --> src/main.rs:9:10
  |
9 |     &t * &t
  |          ^^

error[E0490]: a value of type `<&T as std::ops::Add<&'a T>>::Output` is borrowed for too long
 --> src/main.rs:9:10
  |
9 |     &t * &t
  |          ^^
  |
note: the type is valid for the lifetime 'b as defined on the function body at 3:1
 --> src/main.rs:3:1
  |
3 | / fn double_and_square<'a, 'b, T>(x: &'a T) -> <&'b <&'a T as Add>::Output as Mul>::Output
4 | | where
5 | |     &'a T: Add,
6 | |     &'b <&'a T as Add>::Output: Mul,
... |
9 | |     &t * &t
10| | }
  | |_^
note: but the borrow lasts for the lifetime 'a as defined on the function body at 3:1
 --> src/main.rs:3:1
  |
3 | / fn double_and_square<'a, 'b, T>(x: &'a T) -> <&'b <&'a T as Add>::Output as Mul>::Output
4 | | where
5 | |     &'a T: Add,
6 | |     &'b <&'a T as Add>::Output: Mul,
... |
9 | |     &t * &t
10| | }
  | |_^
Run Code Online (Sandbox Code Playgroud)

我读的方式the lifetime must be valid for the lifetime 'b as defined on the function body告诉我编译器认为'b应该和整个函数体一样长或者更长,而我只是想让它代表"任何生命周期".

我想在Rust中做什么甚至可能?如果没有,是否有任何建议的更改我应该注意哪些可以实现?

She*_*ter 4

系好安全带...

use std::ops::{Add, Mul};

fn double_and_square<'a, T, R>(x: &'a T) -> R
where
    &'a T: Add,
    for<'b> &'b <&'a T as Add>::Output: Mul<Output = R>,
{
    let t = x + x;
    &t * &t
}
Run Code Online (Sandbox Code Playgroud)

很容易,对吧?;-)

让我们一步一步来......

  1. 您希望获取对类型的引用,但该引用需要实现Add. where子句允许您在 的两侧编写复杂类型:,因此我们使用&'a T: Add.

  2. 这将返回一些我们需要另一个引用的值。但是,调用double_and_square无法指定生存期,因为它只存在于函数内部。这意味着我们需要使用更高排名的特征界限for <'b>

  3. 我们必须使用操作的输出类型Add,假设它实现了Mul,并且输出类型是通用的R


我建议不要在原始函数中引用,因为它更容易理解:

fn double_and_square<T, R>(x: T) -> R
where
    T: Add + Copy,
    for<'a> &'a T::Output: Mul<Output = R>,
{
    let t = x + x;
    &t * &t
}
Run Code Online (Sandbox Code Playgroud)

&Foo 一个独立的类型Foo,可以作为 的具体类型传递T,所以这应该能够在原始的任何地方使用,并且可能在更多情况下可用。

我希望它适用于TCopy

Copy即使类型本身没有实现,对类型的引用始终是不可变的Copy。因此,您可以使用 egT = i32 a来调用此函数T = &NonCopy接受参考文献的原始案例将只接受第二个。

在理想的世界中,您可以避免使用泛型类型R,而只需说<...something...>::Output,但据我所知,目前这是不可能的。