1
votes

I have a trait with a function, and this function takes a closure as an argument, and that closure takes an argument that needs to be some type implementing the Read trait:

trait CanRead {
    type Reader: io::Read;
    
    fn do_reading<F>(&mut self, fun: F)
    where F: FnOnce(&mut Self::Reader);
}

I can easily implement this trait for anything which already implements Read, by specifying type Reader = Self; and just running fun(self); in the do_reading function.

The challenge is, I want to also implement this trait for some type which must make a temporary vector of u8s. Then the associated type Reader needs to be a reference type, but I don't know what lifetime to give it:

pub struct EmptyStruct { }

impl CanRead for EmptyStruct {
    type Reader = &[u8]; // doesn't compile; must specify a lifetime here
    
    fn do_reading<F>(&mut self, fun: F)
    where F: FnOnce(&mut Self::Reader) {
        let temp = vec![1, 2, 3];
        fun(&mut &temp[..]);
    }
}

I know that I need to specify a lifetime, but what could it be? I looked at this helpful related question, but neither suggested approach works. The problem is that the lifetime of the Reader type actually has nothing to do with the lifetime of the EmptyStruct instance; instead, the Reader reference type just needs to not outlive the call to the closure itself. Is there some way to specify this in Rust, or another way to tackle this pattern?

playground has what I tried and didn't work.

(Note, I know for this specific code the temp vector could be replaced by a static array, but that won't work for what I really need to do.)

2

2 Answers

1
votes

Well, unfortunately you can't yet. The referenced answer noted a GAT which will be a solution (as soon as it implemented). Here's an rfc (and tracking issue) what covers that case.

0
votes

Here is my current work-around. I hope that someone answers with a better solution than this!

A work-around is to use dynamic dispatch, avoiding the associated type entirely, like so:

pub trait CanRead {
    fn do_reading<F>(&mut self, fun: F)
    where F: FnOnce(&mut dyn io::Read);
}

pub struct EmptyStruct { }

impl CanRead for EmptyStruct {
    fn do_reading<F>(&mut self, fun: F)
    where F: FnOnce(&mut dyn io::Read) {
        let temp = vec![1, 2, 3];
        fun(&mut &temp[..]);
    }
}

The downside here is that, because the closure's access is through a &mut dyn reference, you lose some run-time efficiency because of dynamic dispatch. Granted, the penalty is probably not too large, but it would be better to use Rust's powerful type system and generics to avoid this if possible.