简体   繁体   中英

Why does the compiler need an implementation of a trait to call a default free function?

When calling a default implementation on a trait which does not take self , why does it neeed an implementing type to be annotated?

A minimal, reproducible example is below ( playground ):

mod builder {
    pub trait Builder: Sized {
        fn new() -> Simple {
            Simple
        }
    }

    pub struct Simple;

    impl Builder for Simple {}
}

pub fn main() {
    let _ = builder::Builder::new();
    
    /* Working version */
    // use builder::Builder;
    // let _ = builder::Simple::new();
}

Which gives:

error[E0283]: type annotations needed
  --> src/main.rs:14:13
   |
3  |         fn new() -> Simple {
   |         ------------------ required by `builder::Builder::new`
...
14 |     let _ = builder::Builder::new();
   |             ^^^^^^^^^^^^^^^^^^^^^ cannot infer type
   |
   = note: cannot satisfy `_: builder::Builder`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0283`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

The compiler explanation for E0283 does not mention a default implementation, which I agree it makes sense. But for default implementations, why is a type required?

This is not only a default implementation but the very specific case in which this default implementation does not even mention Self / self in its parameters, result and body.

I find much more easy to understand a rule saying that a type is required every time we use a trait, in any case, rather that « except if the default implementation does not even mention Self / self in its parameters, result and body » .

For this very specific use case, where you do not want to explicitly name a type when calling the function you need, I suggest using a free function.

mod builder {
    // ...
    pub fn make_default() -> Simple {
        Simple
    }
    // ...
}

pub fn main() {
    let _ = builder::make_default();
}

Provided methods in Rust are not like static methods in Java. Even a function with no arguments and a default implementation can be overridden by implementors. Consider adding another type that implements Builder but overrides the new function:

struct Noisy {}

impl builder::Builder for Noisy {
    fn new() -> builder::Simple {
        println!("Ahahahah I'm creating a Simple!!!11!");
        builder::Simple
    }
}

fn main() {
    // wait, should this call builder::Simple::new() or Noisy::new()?
    let _ = builder::Builder::new();
}

If you want the effect of a Java static function that always has the same behavior, you should use a free function, as prog-fh's answer also suggests.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM