Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning a generic struct from new

Tags:

generics

rust

I want to return a generic struct. The idea is that I want to have the actual implementation output to STDIN per the standard, but for testing purposes I'd like to substitute it with a vector or something else so I can easily test the output. The solution I have so far is:

#[derive(Debug)]
pub struct Sender<R, W> {
    pub reader: R,
    pub writer: W,
}

#[derive(Debug)]
pub struct Sender<R, W> {
    pub reader: R,
    pub writer: W,
}

impl<R, W> Sender<R, W> {
    pub fn new<R, W>(reader: R, writer: W) -> Sender<R, W> {
        Sender {
            reader: R,
            writer: W,
        }
    }
}

The errors I'm getting:

error[E0423]: expected value, found type parameter `R`
  --> src/main.rs:10:21
   |
10 |             reader: R,
   |                     ^ not a value

error[E0423]: expected value, found type parameter `W`
  --> src/main.rs:11:21
   |
11 |             writer: W,
   |                     ^ not a value

error[E0601]: main function not found

error[E0194]: type parameter `R` shadows another type parameter of the same name
 --> src/main.rs:8:16
  |
7 | impl<R, W> Sender<R, W> {
  |      - first `R` declared here
8 |     pub fn new<R, W>(reader: R, writer: W) -> Sender<R, W> {
  |                ^ shadows another type parameter

error[E0194]: type parameter `W` shadows another type parameter of the same name
 --> src/main.rs:8:19
  |
7 | impl<R, W> Sender<R, W> {
  |         - first `W` declared here
8 |     pub fn new<R, W>(reader: R, writer: W) -> Sender<R, W> {
  |                   ^ shadows another type parameter

This makes sense to me, it doesn't know the concrete type of R. I figured giving the generic parameters in the function would have solved that though, but that gives this additional error:

34 | impl<R, W> Sender<R, W> {
   |          - first `R` declared here
35 |   pub fn new<R, W>(reader: R, writer: W) -> Sender<R, W> {
   |              ^ shadows another type parameter

That doesn't work because new<R, W> declares a different (but same named!) R and W.

Is there a way to do this that doesn't require me to make two different new functions, one for memory read/write and the other for STDIN read/write?


1 Answers

I think you may be misunderstanding the syntax used to initialise structs - as it stands, your function is trying to set the reader and writer fields of the struct to R and W, not the parameters that you're passing in!

It should look like this:

impl<R, W> Sender<R, W> {
    pub fn new(reader: R, writer: W) -> Sender<R, W> {
        Sender {
            reader: reader, // field name on the left, value on the right
            writer: writer,
        }
    }
}

Because the struct field and your parameters have the same names, you can also use the field init shorthand syntax:

impl<R, W> Sender<R, W> {
    pub fn new(reader: R, writer: W) -> Sender<R, W> {
        Sender { reader, writer }
    }
}

I'd recommend reading Chapter 5 of 'The Rust Programming Language' if you haven't already - it covers all of this stuff.

like image 186
Joe Clay Avatar answered Oct 22 '25 06:10

Joe Clay