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

时间:2015-05-09 16:05:52

标签: rust

这有效:

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/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`

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

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

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

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

2 个答案:

答案 0 :(得分:4)

作为@VladimirMatveev says,闭包的返回类型是关联类型。

关联类型与类型参数不同,因为其值是在实施特征时确定的,而不是在您在通话中使用时确定的。
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编译是不安全的。 / p>

现在假装你有一个

struct Foo<T> { t: T }

T可以是任何内容,包括保存引用的类型 特别是,T可以是类似& i32的类型,也可以是类似&Cell<&i32>的类型 与我们上面的foo函数一样,Rust可以通过检查Tplayground)的类型来推断它可以或者不能让我们分配更短的生命周期。

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

如果你有

struct Foo<T>; // unused type parameter!
如果您希望PhantomType表现得像T& i32那样,

Rust会要求您使用Cell进行指定。你会写:

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>>
}

答案 1 :(得分:0)

通过特征对象使用动态分派的另一种方法是使用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

你&#34;实例化&#34;只需使用PhantomDataPhantomData,例如

let phantom: PhantomData<T> = PhantomData;