Rust: Rust complains about unused type parameter which is only used to parameterize another

Created on 10 Mar 2015  路  7Comments  路  Source: rust-lang/rust

Code:

$ cat typetest.rs 
struct Foo<Elem, List:AsSlice<Elem>> {
    elems: List,
}

fn main() {
}

Compile:

$ rustc typetest.rs 
typetest.rs:1:12: 1:16 error: parameter `Elem` is never used
typetest.rs:1:16: 1:16 help: consider removing `Elem` or using a marker such as `core::marker::PhantomData`
error: aborting due to previous error

I'm pretty new to rust, so forgive me if I've missed something. But I can't see a way to say "my struct contains a type T2, which implements SomeGenericTrait<T>" without my struct also being parameterized over T, as I'm doing here. But rust thinks that I'm not using T.

I tried adding:

dummy: Elem,

to my struct def, which makes it compile. So it doesn't look like this is just one error message obscuring another, rust does really think this is the only thing wrong with my code.


$ rustc --version --verbose
rustc 1.0.0-dev (2fc8b1e7c 2015-03-07) (built 2015-03-08)
binary: rustc
commit-hash: 2fc8b1e7c4ca741e59b144c331d69bf189759452
commit-date: 2015-03-07
build-date: 2015-03-08
host: x86_64-unknown-linux-gnu
release: 1.0.0-dev

Most helpful comment

For those who will look here for answers:
"In most cases you do not need additional type parameters at all".
That's it. Just remove them.

Taking @Piping example for illustration.
Let's get rid of type parameter for Quicksort.

struct Quicksort;

Now rustc obviously won't complain about unused parameters since there are none.
But at some point we actually need that type-parameter.

Continuing. Type parameter can be introduced at function level.

impl Quicksort {
  pub fn sort<T>(&self,ar<T>){
    Sorter::sort(self, ar<T>) ;
  }
}

Now when it comes to implementing a trait things goes smoothly again since type parameter is already exists in Sorter trait.

impl<T> Sorter<T> for Quicksort {
 fn join(...) {...}
 fn split(...) {...}
}

There are few places where you actually need artificial type parameter.

  1. When type conceptually contains some generic type.
  2. Type must implement a trait with associated type.
trait Trait {
  type X;
}

Now either the type in question will be able to implement the Trait with some predefined X.

struct Foo;

impl Trait for Foo {
  type X = u32;
}

Or it has to have this artificial type parameter after all.

struct Foo<T>(PhantomData<T>);

impl<T> Trait for Foo<T> {
  type X = T;
}

All 7 comments

This is actually intended behavior due to a recently merged RFC. The idea here is that the compiler needs to know what constraints it can put on the type parameter Elem, and usage of the PhantomData type will instruct the compiler how it can do so.

You can check out the examples for PhantomData to see some usage, and in this case you'll probably want to do something along the lines of:

  1. Refactor the struct so unused type parameters aren't needed. This can often be done with associated types, something along the lines of:

rust trait A { type B; } struct C<T: A> { inner: A::B, }

  1. Use PhantomData<Elem> to indicate that the compiler should just consider the type parameter used, considering Foo<A, B> as containing something of type A as well as B.

You probably don't want to store dummy: Elem as it will require you to store an extra element and could have other memory implications as well.

Thanks for the helpful explanation, @alexcrichton. You also conveniently educated me on associated types, which I was wondering about but didn't know what to search for (I was wondering "why is Iterator not generic"). It turns out this is just what I needed.

I just stumbled upon this limitation in a context where PhantomData is of no help: generic enums. Consider the following piece of code:

enum Foo<A, B>
    where A: Bar<B> + Sized
{
    X(A),
    Y,
    Z
}

trait Bar<B> {
   fn bar(&self, &B);
}

Edit: I'm aware of several workarounds. These include adding PhantomData to X, but this pollutes the enum (in the sense that construction of a value by the user becomes iffy).

Hi, I had a similar problem regarding to unused type parameter.
Here is the simplified code, I need to control the visibility for quicksort's sort function.

struct Quicksort<T>;  // <---- unused parameter warning
trait Sorter<T> {
  fn sort(&self,ar<T>){...}
  fn join(...);
  fn split(...);
}
impl<T> Quicksort<T> {
  pub fn sort(&self,ar<T>){
    Sorter::sort(self, ar<T>) ;
  }
}
impl<T> Sorter<T> for Quicksort<T>{
 fn join(...) {...}
 fn split(...) {...}
}

Any help to resolve the problem? because struct Quicksort does not have any fields. The type parameter is solely for the sort function from trait.
Otherwise if I removed impl<T> Quicksort<T>, then I don't need the T for Quicksort at all.

For those who will look here for answers:
"In most cases you do not need additional type parameters at all".
That's it. Just remove them.

Taking @Piping example for illustration.
Let's get rid of type parameter for Quicksort.

struct Quicksort;

Now rustc obviously won't complain about unused parameters since there are none.
But at some point we actually need that type-parameter.

Continuing. Type parameter can be introduced at function level.

impl Quicksort {
  pub fn sort<T>(&self,ar<T>){
    Sorter::sort(self, ar<T>) ;
  }
}

Now when it comes to implementing a trait things goes smoothly again since type parameter is already exists in Sorter trait.

impl<T> Sorter<T> for Quicksort {
 fn join(...) {...}
 fn split(...) {...}
}

There are few places where you actually need artificial type parameter.

  1. When type conceptually contains some generic type.
  2. Type must implement a trait with associated type.
trait Trait {
  type X;
}

Now either the type in question will be able to implement the Trait with some predefined X.

struct Foo;

impl Trait for Foo {
  type X = u32;
}

Or it has to have this artificial type parameter after all.

struct Foo<T>(PhantomData<T>);

impl<T> Trait for Foo<T> {
  type X = T;
}

@neithernut

In case you're still interested, I had a similar problem and I solved it by creating an additional variant that stores the PhantomData and a never type Empty:

pub enum Empty {}
pub enum Foo<A, B> {
    X(A),
    // ...
    Void(Empty, PhantomData<B>),
}

playground example

The good news is that it's zero-cost and does not pollute the variants you do use.

The bad news is that, until never type exhaustive matching comes to Rust, this approach will pollute the code when you match on your enum (although you could macro your way around it):

match foo {
    Foo::A(_) => (),
    // ...
    Foo::Void(empty, _) => match empty {},
    //               ^
    // phantom data
}

@alexcrichton
I also had some issues with this and generic enums. It would be very useful if the compiler could recognize that the type parameter is only used in a trait bound for another type parameter. I don't understand the reason why this would not work now.. Can't the compiler derive the constraints from the trait the type parameter is passed to?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zhendongsu picture zhendongsu  路  3Comments

dwrensha picture dwrensha  路  3Comments

Robbepop picture Robbepop  路  3Comments

modsec picture modsec  路  3Comments

drewcrawford picture drewcrawford  路  3Comments