0

I'm trying to create a trait with some default method implementation. One of the methods has to take an instance of the same type and perform some computations.

Here is the simplistic example of what I'm trying to achieve:

struct A { val: f32, } trait S { fn val(&self) -> f32; fn add(&self, other: &Self) -> f32 { add(&self, other) } } impl S for A { fn val(&self) -> f32 { self.val } } fn add<T: S>(first: &T, second: &T) -> f32 { first.val() + second.val() } 

This fails to compile with the error:

15 | | fn add(&self, other: &Self) -> f32 { 16 | | add(&self, other) | | ^^^^^ expected `&Self`, found type parameter `Self` 17 | | } 

I do not understand the error message, for other is of type &Self not Self, so why does compiler thinks otherwise?

If I change it to reference add(&self, &other) (which doesn't seem right, since other is already a reference type), I get another error:

 | 16 | add(&self, &other) | ^^^ the trait `S` is not implemented for `&Self` ... 26 | fn add<T: S>(first: &T, second: &T) -> f32 { | - required by this bound in `add` 

Is there a way to achieve this with default trait implementation, or this can work only with concrete types (like in this question: How do I implement the Add trait for a reference to a struct?)?

EDIT: If I call it like add(self, other), it tries to send the trait object, but I want to send the objects by reference. And really, I want to send concrete implementations and not trait objects.

error[E0277]: the size for values of type `Self` cannot be known at compilation time --> src/main.rs:16:9 | 16 | add(self, other) | ^^^ doesn't have a size known at compile-time 
1
  • 1
    If I call it like add(self, other), it tries to send the trait object - how did you come to that conclusion? I ask because I don't think it's correct. The compiler reserves the possibility of Self being an unsized object, which would make the &Self reference variably-sized. But, as the compiler explains, you can easily opt out of that feature either by requiring Sized in the trait. Commented Sep 16, 2021 at 8:00

1 Answer 1

2

You're passing &self to add(), which makes it a double reference and the arguments to add() don't agree in types. The T of add is apparently determined by the first argument, which is why the compiler appears to expect the other argument to also have the &&Self parameter. (Read the note at the end of the error message.)

Call it with add(self, other) and it will compile. Note that you'll also need to opt out of the implicit Sized bound on add by adding + ?Sized.

// rest as in your code trait S { fn val(&self) -> f32; fn add(&self, other: &Self) -> f32 { add(self, other) } } fn add<T: S + ?Sized>(first: &T, second: &T) -> f32 { first.val() + second.val() } 

Playground

Sign up to request clarification or add additional context in comments.

2 Comments

It seems I misunderstood the meaning of Sized trait, from that the confusion about trait objects. I still don't understand what ?Sized means, but at least I know now what to search for. Thanks.
I still don't understand what ?Sized means, - T: ?Sized means that T is allowed to be unsized, the ? removes the implicit Sized bound on the type parameter T. In this case it allows add() to work with trait objects - but doesn't mean that it requires trait objects or that it somehow converts references to trait objects; it will accept references to concrete types (that happen to implement S) just fine.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.