闭包参数上未使用的类型参数

Unused type parameter on closure argument

这个有效:

struct Foo<T, F>
where
    F: Fn() -> Option<T>,
{
    f: F,
}

但这给了我编译错误:

struct Bar<I, T, F>
where
    F: Fn(I) -> Option<T>,
{
    f: F,
}
error[E0392]: parameter `I` is never used
 --> src/lib.rs:1:12
  |
1 | struct Bar<I, T, F>
  |            ^ unused parameter
  |
  = help: consider removing `I`, referring to it in a field, or using a marker such as `std::marker::PhantomData`

error[E0392]: parameter `T` is never used
 --> src/lib.rs:1:15
  |
1 | struct Bar<I, T, F>
  |               ^ unused parameter
  |
  = help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData`

为什么在闭包的 return 类型中使用类型参数可以,但在其参数中却不行?

我可以通过将闭包存储为特征对象来绕过它:

struct Bar<I, T> {
    f: Box<Fn(I) -> Option<T>>,
}

但我想尽可能避免这种情况。

通过 trait 对象使用动态调度的替代方法是使用 std::marker::PhantomData

use std::marker::PhantomData;

struct Bar<I, T, F>
where
    F: Fn(I) -> Option<T>,
{
    f: F,
    _i: PhantomData<I>,
    _t: PhantomData<T>,
}

(playground)

您 "instantiate" 一个 PhantomData 只需使用 PhantomData,例如

let phantom: PhantomData<T> = PhantomData;

一样,闭包的return类型是关联类型。

关联类型不同于类型参数,因为它的值是在您实现特征时确定的,而不是在调用中使用它时确定的。
Fn(I) -> Option<T> 中,一旦您有了输入(I 类型)和实现(您传递的闭包中定义的特定操作),Option<T> 输出就确定了。

但是 I 情况不同。您需要使用结构中的类型,或者向编译器展示 理论上如何使用它,并带有 PhantomData 字段。

use std::marker::PhantomData;

struct Bar<I, T, F>
where
    F: Fn(I) -> Option<T>,
{
    f: F,
    _marker: PhantomData<I>,
}

PhantomData 仅用于检查类型,但在生成的代码中被删除,因此它不会占用您的结构中的任何内存(这就是为什么它是幻影)。

需要它的原因在RFC 738 on variance中有详细解释。我会尽量在这里给你一个更短的(希望是正确的)版本。

在 Rust 中,大多数时候(但不总是!)您可以在预期较短的生命周期中使用较长的生命周期。

fn foo<'short, 'long>(_a: &'short i32, b: &'long i32)
where
    'long: 'short,
{
    let _shortened: &'short i32 = b; // we're binding b to a shorter lifetime
}

fn foo2<'short, 'long>(_a: &'short i32, b: &'long Cell<&'long i32>)
where
    'long: 'short,
{
    let _shortened: &Cell<&'short i32> = b;
}

(playground)

RFC 解释了为什么 Cell 期望完全相同(而不是更长)的生命周期,但现在我建议您只相信编译器允许 foo2 编译是不安全的.

现在假设你有一个

struct Foo<T> { t: T }

T 可以是任何东西,包括包含引用的类型。
特别是,T 可以是 & i32 类型或 &Cell<&i32>.
类型 与我们上面的 foo 函数一样,Rust 可以通过检查 T (playground) 的类型来推断它何时允许或不允许我们分配更短的生命周期。 =38=]

但是,当您有一个未使用的类型参数时,推理没有任何字段可以检查以了解它应该如何允许类型在生命周期内表现。

如果你有

struct Foo<T>; // unused type parameter!

Rust 要求您使用 PhantomType 指定您是否希望 T 表现得像 & i32Cell。你会写:

struct Foo<T> {
    marker: PhantomData<T>, // this is what you usually want
                            // unless you're working with unsafe code and
                            // raw pointers
}

或者你可以这样写:

struct Foo<T> {
    marker: PhantomData<Cell<T>>
}