Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Expected type parameter" error in the constructor of a generic struct

Tags:

I am trying to store piston textures in a struct.

struct TextureFactory<R> where R: gfx::Resources {     block_textures: Vec<Rc<Texture<R>>>, }  impl<R> TextureFactory<R> where R: gfx::Resources  {     fn new(window: PistonWindow) -> Self {         let texture = Rc::new(gfx_texture::Texture::from_path(             &mut *window.factory.borrow_mut(),             "assets/element_red_square.png",             Flip::None, &TextureSettings::new()         ).unwrap());         let block_textures = Vec::new();         block_textures.push(texture);          TextureFactory {             block_textures: block_textures,         }     } } 

This does not compile:

src/main.rs:37:9: 39:10 error: mismatched types:  expected `TextureFactory<R>`,     found `TextureFactory<gfx_device_gl::Resources>` (expected type parameter,     found enum `gfx_device_gl::Resources`) 

gfx_device_gl::Resources implements gfx::Resources though (I think it's just the device specific implementation.) I don't actually care what type this is, but I need to know so that I can store it in the struct.

I made a compilable repo on Github.

(I suspect Rust generics/traits: "expected 'Foo<B>', found 'Foo<Foo2>'" is the same question, but I can't figure out how to apply it to my problem.)

like image 254
Xavier Shay Avatar asked Sep 13 '15 15:09

Xavier Shay


1 Answers

Here's a reproduction of your error:

struct Foo<T> {     val: T, }  impl<T> Foo<T> {     fn new() -> Self {         Foo { val: true }     } }  fn main() {} 

The problem arises because you tried to lie to the compiler. This code:

impl<T> Foo<T> {     fn new() -> Self {         /* ... */     } } 

Says "For whatever T the caller chooses, I will create a Foo with that type". Then your actual implementation picks a concrete type — in the example, a bool. There's no guarantee that T is a bool. Note that your new function doesn't even accept any parameter of type T, which is highly suspect as that's how the caller picks the concrete type 99% of the time.

The correct way of saying this would be

impl Foo<bool> {     fn new() -> Self {         Foo { val: true }     } } 

Although you probably want to pick a more specific name than new, as it looks as if you are trying to make your struct generic. Presumably there would be other constructors with different types.

For your exact code, you probably want something like

impl TextureFactory<gfx_device_gl::Resources> { /* ... */ } 

Another possible solution would be to remove the generic type parameter from your struct. If you only ever construct it with a gfx_device_gl::Resources, then there's no reason to make it generic.

In other cases, you may be trying to return a type that implements a trait. For that, you can use a boxed trait object:

impl Foo<Box<dyn std::fmt::Display>> {     fn new() -> Self {         Foo { val: Box::new(true) }     } } 

In the future, you may also be able to use impl Trait (a.k.a. existential types):

#![feature(type_alias_impl_trait)]  struct Foo<T> {     val: T, }  type SomeConcreteButOpaqueType = impl std::fmt::Display;  impl Foo<SomeConcreteButOpaqueType> {     fn new() -> Self {         Foo { val: true }     } } 

See also:

  • What is the correct way to return an Iterator (or any other trait)?
like image 190
Shepmaster Avatar answered Oct 26 '22 05:10

Shepmaster