Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API user must import trait when API switches from Box<Trait> to impl Trait #54610

Closed
hniksic opened this issue Sep 27, 2018 · 1 comment
Closed

Comments

@hniksic
Copy link
Contributor

hniksic commented Sep 27, 2018

While switching an API from returning Box<Trait> to returning impl Trait (it was using Box only to isolate the class implementing the trait), I noticed an incompatibility. The user of the API is now required to import the trait in order to call method defined by the trait, which slightly reduces the ergonomy of the API.

Take this code, which compiles fine:

mod x {
    use std::io::Write;

    struct Opaque;

    impl Write for Opaque {
        fn write(&mut self, _: &[u8]) -> ::std::io::Result<usize> {
            Ok(1)
        }
        fn flush(&mut self) -> ::std::io::Result<()> {
            Ok(())
        }
    }

    pub fn get_writer() -> Box<Write> {
        Box::new(Opaque)
    }
}

fn main() {
    let mut writer = x::get_writer();
    writer.write_all(b"foo").unwrap();
}

Now, if we just change the return type from Box<Write> to impl<Write>, like this:

mod x {
    use std::io::Write;

    struct Opaque;

    impl Write for Opaque {
        fn write(&mut self, _: &[u8]) -> ::std::io::Result<usize> {
            Ok(1)
        }
        fn flush(&mut self) -> ::std::io::Result<()> {
            Ok(())
        }
    }

    pub fn get_writer() -> impl Write {
        Opaque
    }
}

fn main() {
    let mut writer = x::get_writer();
    writer.write_all(b"foo").unwrap();
}

it no longer compiles:

rustc -O test2.rs 
error[E0599]: no method named `write_all` found for type `impl std::io::Write` in the current scope
  --> test2.rs:22:12
   |
22 |     writer.write_all(b"foo").unwrap();
   |            ^^^^^^^^^
   |
   = help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
   |
1  | use std::io::Write;
   |

error: aborting due to previous error

While I can understand the problem, and know how to fix it on the caller side (just use std::io::Write), I am curious why the import was not required when returning a Box. Of course, switching the return is a backward incompatible change anyway (it would break code that explicitly typed writer as Box<Write>), but for simple usages it would be much nicer if the user could just call the method without explicitly importing the trait, as was possible previously.

So in this case switching from boxed return to impl return has slightly reduced the ergonomy of the API. Is there a way to prevent that?

@petrochenkov
Copy link
Contributor

Duplicate of #41221

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants