How to move dyn Read into thread?

I create a reader wrapper that has Box<dyn Read> inside, and try to put it into a thread:

let fp = ProgressFile::open(Path::new(&path))?;
let reader = BufReader::new(match extension {
	"gz" => Box::new(GzDecoder::new(fp)?) as Box<dyn Read>,
	"bz2" => Box::new(BzDecoder::new(fp)) as Box<dyn Read>,
	_ => Box::new(fp) as Box<dyn Read>
});
// ...
let reader_thread = thread::spawn(move || -> Result<(), BgIterError<OsmObj>> {
	for obj in reader {
		// ...
	}
});

It does not compile, because to move reader into the thread, it has to send Box<dyn Read>, and it's impossible. (Similar thread here.)

286 |         let reader_thread = thread::spawn(move || -> Result<(), BgIterError<OsmObj>> {
    |                             ^^^^^^^^^^^^^ `(dyn std::io::Read + 'static)` cannot be sent between threads safely
    |
    = help: the trait `Send` is not implemented for `(dyn std::io::Read + 'static)`
    = note: required because of the requirements on the impl of `Send` for `Unique<(dyn std::io::Read + 'static)>`
    ...
note: required by a bound in `spawn`

Question: what to do?

I guess, adding Send trait to Box<dyn Read> + 'static is impossible?

Should I put match instead of every ?, take the error message and re-wrap it into a new general error type?

Or should I just put them into enum? (does this make any sense?)

What you want is a Box<dyn Read + Send>. Whether that is valid depends on if what you're putting into the Box implements those two traits. In particular, it must actually implement Send - so find yourself a "thread-safe" reader ig.

3 Likes

It's fine, if the erased type is Send. You add it to the dyn trait object itself. Does something like this work?

let reader = BufReader::new(match extension {
	"gz" => Box::new(GzDecoder::new(fp)?) as Box<dyn Read + Send>,
	"bz2" => Box::new(BzDecoder::new(fp)) as Box<dyn Read + Send>,
	_ => Box::new(fp) as Box<dyn Read + Send>
});
// ...and any other instances of `Box<dyn Read>`...
3 Likes

That did the trick! Thanks!

Sad that it's not clear from the error report, whether it's impossible completely (I thought it could have been because of 'static, or it's unimplemented in std types, or it's just a missing type bound.

In particular, this like made me think I'd have hit some blocking limitation:

= help: the trait `Send` is not implemented for `(dyn std::io::Read + 'static)`

I agree the help could be better. #75001 is a related case; you could file a new issue with similar tags.

1 Like

Well, I do think it's pretty clear; it's the exact same error you would have got had you used a generic type with the Read bound but without Send.

In particular, error message says that it can't prove that Box<dyn Read>: Send. It doesn't say anything about Box<dyn Read + Send>, and it doesn't assert anywhere that it's impossible to declare or construct a value of type Box<dyn Read + Send> (which has to be Send, by the definition of trait objects).

2 Likes