2

I'm trying to update some older code I wrote that basically looks like:

trait Foo<T>{}

struct Bar<A, B: Foo<A>>{
  b: B
}

This used to work totally fine, but now I am getting a compile error:

src/test.rs:19:12: 19:13 error: parameter `A` is never used
src/test.rs:19 struct Bar<A, B: Foo<A>> {
                          ^
src/test.rs:19:12: 19:13 help: consider removing `A` or using a marker such as `core::marker::PhantomData`

So I can try to remove the type parameter and get something like this:

struct Bar<A>{
  b: Foo<A>
}

however this is not really what I want. In my original code B resolves to a sized type, but now Foo<A> is unsized.

The other suggested solution is to try using this PhantomData the error mentions, resulting in:

struct Bar<A, B: Foo<A>> {
  b: B,
  marker: PhantomData<A>
}

but this seems really messy to me. Reading the docs for PhantomData seem to indicate this is meant to be used with unsafe code, but I am not working with unsafe code anywhere here. All I want is for Bar to contain an instance some type that implements Foo.

Is this really the only way to handle this situation now, or am I missing something?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Dan Simon
  • 12,891
  • 3
  • 49
  • 55

2 Answers2

4

Depending on how your real Foo is, you might be able to work with associated types instead, like this:

trait Foo{ type T; }

struct Bar<B: Foo> {
  b: B,
}

otherwise (if you do need to have the type parameter on Foo), PhantomData is indeed the way to go.

You were not the only person finding PhantomData's docs confusing (see PhantomData is incomprehensible). As a result, the documentation for PhantomData has been recently improved by Steve Klabnik and now it does mention this scenario explicitly (and not just unsafe code).

mcarton
  • 27,633
  • 5
  • 85
  • 95
Paolo Falabella
  • 24,914
  • 3
  • 72
  • 86
  • Looks like the associated type is working for me. I did see the issue and updated docs about PhantomData and still think it's pretty weird, but at least I can avoid it for now :) – Dan Simon Apr 26 '15 at 16:55
  • We might look into making it less weird in the future, but for now, it's a fairly low-level kind of thing, for sure. – Steve Klabnik Apr 27 '15 at 21:31
2

It seems that what you want is the following: for any type A, B must implement Foo<A>. That suggests that you rely on functionality of Foo that does not depend on the value of A, which means that you can change Foo to have an associated type rather than a type parameter.

trait Foo<T> { }

will become

trait Foo {
    type T;
}

Then you can remove the A type parameter everywhere and require just B: Foo.

To elaborate on PhantomData, it has nothing to do with unsafe code, it is used to determine variance of a type parameter when the compiler cannot infer it. See RFC 738 for more information on variance and PhantomData.

Ruud
  • 3,118
  • 3
  • 39
  • 51