Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it better to specify trait bound on the impl block or on the method?

Tags:

rust

traits

Suppose I want to create some type that wraps some other generic type, like so:

struct MyWrapper<T> {
    pub inner: T,
}

Now I want my type to have a method if the inner type satisfies a specific bound. For example: I want to print it (in this example without using fmt traits for simplicity). To do this I have two possibilities: adding a bound to the impl or to the method itself.

Method Bound

impl<T> MyWrapper<T> {
    pub fn print_inner(&self) where T: std::fmt::Display {
        println!("[[ {} ]]", self.inner);
    }
}

When calling this function with a MyWrapper<()> I get:

error[E0277]: `()` doesn't implement `std::fmt::Display`
  --> src/main.rs:20:7
   |
20 |     w.print_inner();
   |       ^^^^^^^^^^^ `()` cannot be formatted with the default formatter; try using `:?` instead if you are using a format string
   |
   = help: the trait `std::fmt::Display` is not implemented for `()`

Impl Bound

impl<T: std::fmt::Display> MyWrapper<T> {
    pub fn print_inner(&self) {
        println!("[[ {} ]]", self.inner);
    }
}

Calling it incorrectly again, gives:

error[E0599]: no method named `print_inner` found for type `MyWrapper<()>` in the current scope
  --> src/main.rs:19:7
   |
1  | struct MyWrapper<T> {
   | ------------------- method `print_inner` not found for this
...
19 |     w.print_inner();
   |       ^^^^^^^^^^^
   |
   = note: the method `print_inner` exists but the following trait bounds were not satisfied:
           `() : std::fmt::Display` 

My question is: what is more idiomatic? Are there semantic differences (aside from lifetime stuff with traits, explained here)? Are there differences apart from the compiler message?

like image 386
Lukas Kalbertodt Avatar asked Mar 21 '16 22:03

Lukas Kalbertodt


1 Answers

One semantic difference is that with the type bound on the method you can partially implement a trait:

trait Trait {
    fn f(self) where Self: std::fmt::Display;
    fn g(self);
}

struct Struct<T>(T);

impl<T> Trait for Struct<T> {
    fn f(self) where Struct<T>: std::fmt::Display {
        println!("{}", self);
    }
    fn g(self) {
        println!("Hello world!");
    }
}

fn main() {
    let s = Struct(vec![1]);

    // f is not implemented, but g is
    //s.f();
    s.g();
}

This may be useful if you have many optional methods with different type bounds, which would otherwise require separate traits.

like image 73
starblue Avatar answered Sep 18 '22 23:09

starblue