Struggle with Rust compiler — 10 Link to heading
I have spent hours to figure this out. I hope others can learn more efficiently from my experience.
Consider the following code. Can you predict what the compiler would complain about?
use std::io::Read;
// just a simple struct that holds any Read trait object
pub struct ReadWrapper<R: Read> {
read: R,
}
impl<R: Read> ReadWrapper<R> {
pub fn new(read: R) -> Self {
Self { read }
}
}
// a trait to draw parallel with Read
pub trait Download { /* ... */ }
// a concrete object that implements Download
pub struct Downloader { /* ... */ }
impl Downloader {
pub fn new() -> Self {
Self {}
}
}
impl Download for Downloader { /* ... */ }
// just a simple struct that holds any Download trait object
pub struct DownloadWrapper<D: Download> {
downloader: D,
}
impl<D: Download> DownloadWrapper<D> {
pub fn new(downloader: D) -> Self {
Self { downloader }
}
}
fn main() {
let mut reader = std::io::stdin();
let mut downloader = Downloader::new();
let r_wrapper = ReadWrapper::new(&mut reader);
let d_wrapper = DownloadWrapper::new(&mut downloader);
}
The following is the output from a compiler:
error[E0277]: the trait bound `&mut Downloader: Download` is not satisfied
--> src/main.rs:44:42
|
44 | let d_wrapper = DownloadWrapper::new(&mut downloader);
| -------------------- ^^^^^^^^^^^^^^^ the trait `Download` is not implemented for `&mut Downloader`
| |
| required by a bound introduced by this call
|
note: required by a bound in `DownloadWrapper::<D>::new`
--> src/main.rs:33:9
|
33 | impl<D: Download> DownloadWrapper<D> {
| ^^^^^^^^ required by this bound in `DownloadWrapper::<D>::new`
34 | pub fn new(downloader: D) -> Self {
| --- required by a bound in this associated function
help: consider removing the leading `&`-reference
|
44 - let d_wrapper = DownloadWrapper::new(&mut downloader);
44 + let d_wrapper = DownloadWrapper::new(downloader);
|
error[E0277]: the trait bound `&mut Downloader: Download` is not satisfied
--> src/main.rs:44:21
|
44 | let d_wrapper = DownloadWrapper::new(&mut downloader);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Download` is not implemented for `&mut Downloader`
|
= help: the trait `Download` is implemented for `Downloader`
note: required by a bound in `DownloadWrapper`
--> src/main.rs:29:31
|
29 | pub struct DownloadWrapper<D: Download> {
| ^^^^^^^^ required by this bound in `DownloadWrapper`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` (bin "playground") due to 2 previous errors
Essentially, the compiler does not like the last line. What is more interesting is that the compiler is OK with the second to the last line, which is essentially the same as the last line if we swap Read and Downlaod. Why is it OK with one trait — Read but not the other — Downlaod?
First, let’s try to solve the issue. One easy way to fix is to pass the download object itself rather than as a reference &mut downloader.
let d_wrapper = DownloadWrapper::new(downloader);
This works for this simple example but not the ideal solution, since download is now moved out and cannot be used later in the main function.
A better solution is to write blanket implementation of Download for &mut D for any object D that implements Downlaod trait.
impl<D: Download> Download for &mut D { /* ... */ }
With this addition, the compiler no longer complains. A follow-up question then is how come Read is different?
The answer turns out to be rather simple — the standard library already did this for us here!
impl<R: Read + ?Sized> Read for &mut R {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(**self).read(buf)
}
/* ... */
}
I think the lesson here is that we probably want to do the same for many of the traits we create as well, which will provide the users more flexibility!