“参数`'a`永远不会被使用”'a'用于类型参数绑定时出错

时间:2016-11-08 10:10:47

标签: rust

use std::iter::Iterator;

trait ListTerm<'a> {
    type Iter: Iterator<Item = &'a u32>;
    fn iter(&'a self) -> Self::Iter;
}

enum TermValue<'a, LT>
where
    LT: ListTerm<'a> + Sized + 'a,
{
    Str(LT),
}
error[E0392]: parameter `'a` is never used
 --> src/main.rs:8:16
  |
8 | enum TermValue<'a, LT>
  |                ^^ unused type parameter
  |
  = help: consider removing `'a` or using a marker such as `std::marker::PhantomData`
显然正在使用

'a。这是一个错误,还是参数枚举还没有真正完成? rustc --explain E0392建议使用PhantomData<&'a _>,但我认为在我的用例中没有任何机会这样做。

3 个答案:

答案 0 :(得分:12)

  

'a显然正在使用。

与编译器无关。它所关心的是,所有通用参数都在structenum的正文中使用。约束不计算。

可能想要的是使用排名更高的生命周期界限:

enum TermValue<LT>
where
    for<'a> LT: 'a + ListTerm<'a> + Sized,
{
    Str(LT),
}

在其他情况下,您可能想要使用PhantomData来表示您希望某个类型 使用该参数:

>
use std::marker::PhantomData;

struct Thing<'a> {
    // Causes the type to function *as though* it has a `&'a ()` field,
    // despite not *actually* having one.
    _marker: PhantomData<&'a ()>,
}

要清楚明白:可以PhantomData中使用enum;把它放在其中一个变种中:

enum TermValue<'a, LT>
where
    LT: 'a + ListTerm<'a> + Sized,
{
    Str(LT, PhantomData<&'a ()>),
}

答案 1 :(得分:6)

@DK。回答了如何规避问题(只是按照建议使用PhantomData),并暗示问题是'a在定义中未使用,但为什么编译器会关心它?

'a是一个生命周期标记,借用检查系统使用它来识别不同对象的生命周期之间的关系,以及它们的借用状态。

借用一个对象时,你可以可变地(&mut T)或不可变地(&T)借用它,并按照 Mutability XOR Aliasing 原则支持Rust&# 39;记忆安全它会改变一切:

  • 您可以拥有多个并发&T
  • 您只能拥有一个&mut T,并且会排除并发&T

当您使用struct参数化enum'a时,您宣布您打算借用其生命周期为'a的内容。但是,您不会宣布您是否会以可变或不可变的方式借款,而且这一细节至关重要。

因此,编译器将对应数据类型的内部,并检查是否使用可变或不可变引用来自行推断使用数据类型时将使用哪种借用。

在这里,由于'a未使用,因此无法找到任何此类用途,因此无法编译代码。

编译器在数据类型内窥视是否有好处是有争议的,因为更改此数据类型的内部(从&T&mut T)可能会导致编译失败而不会更改类型界面。

因此,重要的是要记住如何使用通用参数(拥有,可变借用或不可变借用)不是实现细节。

答案 2 :(得分:2)

如果您未在枚举定义中使用关联类型(例如<LT as ListTerm<'a>>::Iter),则可能根本不需要使'a成为参数。

我假设您希望绑定LT: ListTerm<'a>,以便您可以使用fn作为impl编写一个或多个LTListTerm。在这种情况下,您可以使用<LT>轻松地参数化类型,并将'a泛型和特征限制仅限于需要它的项目:

trait ListTerm<'a> {
    type Iter: Iterator<Item = &'a u32>;
    fn iter(&'a self) -> Self::Iter;
}

enum TermValue<LT> {  // no 'a parameter here...
    Str(LT),
}

impl<'a, LT> TermValue<LT>  // ... just here
where
    LT: ListTerm<'a>,
{
    fn iter(&'a self) -> LT::Iter {
        match *self {
            TermValue::Str(ref term) => term.iter(),
        }
    }
}

某些标准库类型(如std::collections::HashMap<K, V>)执行此操作:K: Hash + Eq绑定不属于类型本身。或者,您可以在需要绑定的每个方法上使用where子句。除非您正在实施特征(参见this question),否则where上的impl条款与fn上的条款之间的差异并不重要。

使用PhantomData的主要原因是您希望表达一些编译器无法自行解决的约束。您需要PhantomData表达&#34;任何TermData<LT>只有在其包含的LT有效&#34;时才有效,因为编译器已经强制执行(通过&#34;对等内部&#34;类型,如Matthieu's answer)。