Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Struct with a generic trait which is also a generic trait

Tags:

rust

traits

In Rust 1.15, I have created a trait to abstract over reading & parsing file format(s). I'm trying to create a struct which has this generic trait inside.

I have this trait:

use std::io::Read;

trait MyReader<R: Read> {
    fn new(R) -> Self;
    fn into_inner(self) -> R;

    fn get_next(&mut self) -> Option<u32>;
    fn do_thingie(&mut self);
}

I want to make a struct which has a reference to something that implements this.

struct MyIterThing<'a, T: MyReader<R>+'a> {
    inner: &'a mut T,
}

Gives the following error:

error[E0412]: type name `R` is undefined or not in scope
  --> <anon>:11:36
   |
11 | struct MyIterThing<'a, T: MyReader<R>+'a> {
   |                                    ^ undefined or not in scope
   |
   = help: no candidates by the name of `R` found in your project; maybe you misspelled the name or forgot to import an external crate?

T: MyReader+'a, I get the error: "error[E0243]: wrong number of type arguments: expected 1, found 0", T: MyReader<R: Read>+'a gives a low level syntax error, it's not expecting a : there.

And this doesn't work either:

error[E0392]: parameter `R` is never used
  --> <anon>:11:24
   |
11 | struct MyIterThing<'a, R: Read, T: MyReader<R>+'a> {
   |                        ^ unused type parameter
   |
   = help: consider removing `R` or using a marker such as `std::marker::PhantomData`

How do I create my MyIterThing struct?

like image 543
Amandasaurus Avatar asked Oct 25 '25 13:10

Amandasaurus


2 Answers

You probably don't want a type parameter, you want an associated type:

use std::io::Read;

trait MyReader {
    type R: Read;

    fn new(Self::R) -> Self;
    fn into_inner(self) -> Self::R;

    fn get_next(&mut self) -> Option<u32>;
    fn do_thingie(&mut self);
}

struct MyIterThing<'a, T>
    where T: MyReader + 'a
{
    inner: &'a mut T,
}

fn main() {}

See also:

  • When is it appropriate to use an associated type versus a generic type?
  • Why am I getting "parameter is never used [E0392]"?
  • How can I have an unused type parameter in a struct?
like image 82
Shepmaster Avatar answered Oct 28 '25 03:10

Shepmaster


The error message gives you a suggestion to use a marker, like PhantomData. You can do it like this:

use std::marker::PhantomData;

struct MyIterThing<'a, R: Read, T: MyReader<R> + 'a> {
    inner: &'a mut T,
    marker: PhantomData<R>,
}

Instances of PhantomData have zero runtime cost, so it's better to use that than to just create a field of type R.


Another solution would be to use an associated type instead of a type parameter:

trait MyReader {
    type Source: Read;

    fn new(Self::Source) -> Self;
    fn into_inner(self) -> Self::Source;

    fn get_next(&mut self) -> Option<u32>;
    fn do_thingie(&mut self);
}

struct MyIterThing<'a, T: MyReader + 'a> {
    inner: &'a mut T,
}

This is a little less flexible as there can only be one choice of Source per implementation of MyReader, but it could be sufficient, depending on your needs.

like image 37
Peter Hall Avatar answered Oct 28 '25 02:10

Peter Hall



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!