Composition of several traits with same methods

I have a trait defined as below:

trait Name {
    fn name(&self) -> String {
        "Show".to_string()
    }
}

I have another trait defined as:

trait Page {

    fn name(&self) -> String;
    fn show(&self);
}

How can I tell in the impl of Page for some stuct that name impl will be taken from Name trait?

use UFCS: instead of thing.name(), you can use Name::name(&thing) and Page::name(&thing)

If you always want Page to use Name::name, you could make Page a subtrait of Name:

trait Page: Name {
    fn show(&self);
}

I looked this up and it looks like the opposite of this. In Rust you'd call UFCS "method call syntax" and Name::name might be called "associated function syntax".

Edit: okay apparently this used to mean the exact opposite thing in Rust?

1 Like

Yeah, that might not be the right term.I might've confused it with something else. Anyway, here's the docs for the syntax: Call expressions - The Rust Reference

1 Like

Sort of a partial overlap, but yeah, fully-qualified paths used to be called UFCS in Rust and the usage lingers on.

3 Likes

UFCF works. But I hoped I could avoid a construction like that:

    fn name(&self) -> String {
        Name::name(self)
    }

Such languages as Java can observe that a method with the required name already exists in one sibling and just use it.
Thanks for the help.

I doubt that.

3 Likes

You may want a supertrait relationship.

1 Like

My goal is to reduce a boilerplate code. Currently I use macros containing that code. I thought of riding of macros and incorporate that code in other treat implementation and do just composition of two treats implementation when a boilerplate code absorber trait takes the job. I appreciate if you illustrate a super trait approach of doing that.

I did in my first comment.

Right, but such approach introduces even more boilerplate code.Anyway, I will play more with your approach, because it has some potentials.

How? It's one less function with nothing added. You don't even need to disambiguate the calls to name anymore.

You're confusing commentors, drewtato's answer was supertraits with the example:

trait Page: Name {
    fn show(&self);
}

I see the problem as name should appear in both traits. Actually Page refers it in some its implementations as an abstract method. Now : I introduced a treat as Name, and I want to provide a concrete implementation of name in the treat. So I do not need to provide name in all separate treat implementations. I do not see how I can do that, it's why I asked for more elaborated example.

If you use supertraits you only need to provide the name implementation once (in Name), and users only need to bound on Page to use name. What else do you want?

Why I am getting an error as below:

error[E0407]: method `main_load` is not a member of trait `Name`

Indeed, the method is defined in the trait Page, but Name is inherited from it as below:

trait Name: Page {

Show the code and the full error (always, please!). This compiles:

        trait Page {
            fn main_load(&self);
        }
        trait Name: Page {}

        fn f(name: impl Name) {
            name.main_load();
        }

You wrote Name: Page. This declares that any type implementing Name must implement Page, but it says nothing about some T: Page.

The answer discussed above by drewtato used Page: Name. This says that any T: Page must also implement Name, meaning that those types will have .name() accessible.

trait Name {
    fn name(&self) -> String;
}

//          vvvv
trait Page: Name {
    // .name() will also be callable because
    // Name is a supertrait
    fn show(&self);
}

fn foo<P: Page>(page: &P) {
    let name = page.name();
    println!("{name}");
    page.show();
}
struct RealPage {
   // payload: String
}

trait Page {
    fn content_type(&self) -> String {
        "text/html".to_string()
    }

    fn main_load(&self) -> Result<String, String>;

    fn name(&self) -> String;
}

trait Name: Page {
    fn name(&self) -> String {
        "Show".to_string()
    }
}

impl Name for RealPage {
    fn main_load(&self) -> Result<String, String> {
        Ok(self.name())
    }
}

fn main() {
    println!{"NamePage: {}", RealPage{}.main_load().unwrap()}
}

I do not see much difference from your code which is perfectly compilable.
Errors

error[E0407]: method `main_load` is not a member of trait `Name`
  --> test.rs:22:5
   |
22 | 

/     fn main_load(&self) -> Result<String, String> {
23 | |         Ok(self.name())
24 

| |     }
   | |_____^ not a member of trait `Name`

error[E0277]: the trait bound `RealPage: Page` is not satisfied
  --> test.rs:21:15
   |
21 | 

impl Name for RealPage {
   |               ^^^^^^^^ the trait `Page` is not implemented for `RealPage`

   |

help: this trait has no implementations, consider adding one
  --> test.rs:5:1
   |
5  | trai

t Page {
   | ^^^^^^^^^^

note: required by a bound in `Name`
  --> test.rs:15:13
   |
15 | trait Name: Page {

   |             ^^^^ required by this bound in `Name`


error[E0277]: the trait bound `RealPage: Page` is not satisfied
  --> test.rs:23:17
   |
23 | 

        Ok(self.name())
   |                 ^^^^ the trait `Page` is not implemented for `RealPage`

   |

help: this trait has no implementations, consider adding one
  --> test.rs:5:1
   |
5  | trai

t Page {
   | ^^^^^^^^^^

note: required by a bound in `Name::name`
  --> test.rs:15:13
   |
15 | trait Name: Page {


   |             ^^^^ required by this bound in `Name::name`
16 |     fn name(&self) -> String {


   |        ---- required by a bound in this associated function


error[E0599]: no method named `main_load` found for struct `RealPage` in the current scope
  --> test.rs:28:41
   |
1  

| struct RealPage {
   | --------------- method `main_load` not found for this struct
...
28

 |     println!{"NamePage: {}", RealPage{}.main_load().unwrap()}
   |                                         ^^^^^^^^^ m

ethod not found in `RealPage`

   |

   = help: items from traits can only be used if the trait is implemented and in scope

note: `Page` defines an item `main_load`, perhaps you need to implement it
  --> test.rs:5:1
   |
5  |

 trait Page {
   | ^^^^^^^^^^



  • You can't override a super-trait method in a sub-trait. Implementations in traits are defaults that can be overridden when a type is implemented for that trait.
  • Each sub and super-trait must be implemented separately, for each type. You can't implement a super-trait method when implementing the sub-trait.

So you probably can't do exactly what you wanted. But here is a playground that compiles that you can try modifying.

1 Like