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

Dan*_*mon 6 rust

这有效:

struct Foo<T, F>
where
    F: Fn() -> Option<T>,
{
    f: F,
}
Run Code Online (Sandbox Code Playgroud)

但这给了我编译错误:

struct Bar<I, T, F>
where
    F: Fn(I) -> Option<T>,
{
    f: F,
}
Run Code Online (Sandbox Code Playgroud)
error[E0392]: parameter `I` is never used
 --> src/main.rs:1:12
  |
1 | struct Bar<I, T, F>
  |            ^ unused type parameter
  |
  = help: consider removing `I` or using a marker such as `std::marker::PhantomData`

error[E0392]: parameter `T` is never used
 --> src/main.rs:1:15
  |
1 | struct Bar<I, T, F>
  |               ^ unused type parameter
  |
  = help: consider removing `T` or using a marker such as `std::marker::PhantomData`
Run Code Online (Sandbox Code Playgroud)

为什么在闭包的返回类型中使用类型参数ok,而不是在其参数中?

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

struct Bar<I, T> {
    f: Box<Fn(I) -> Option<T>>,
}
Run Code Online (Sandbox Code Playgroud)

但如果可能的话,我想避免这种情况.

Pao*_*lla 6

正如@VladimirMatveev 所说,闭包的返回类型是关联类型。

关联类型与类型参数不同,因为它的值是在实现特征时确定的,而不是在调用中使用时确定的。
在 中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>,
}
Run Code Online (Sandbox Code Playgroud)

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

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

在 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;
}
Run Code Online (Sandbox Code Playgroud)

操场

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

现在假装你有一个

struct Foo<T> { t: T }
Run Code Online (Sandbox Code Playgroud)

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

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

如果你有

struct Foo<T>; // unused type parameter!
Run Code Online (Sandbox Code Playgroud)

Rust 要求你用 a 指定PhantomType你是否希望你T的行为像 a& i32或像 a Cell。你会写:

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

或者你可以写:

struct Foo<T> {
    marker: PhantomData<Cell<T>>
}
Run Code Online (Sandbox Code Playgroud)