Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

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

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...