I have a trait Foo
. I want to force implementors to define a method, if those implementors implement another trait (Clone
in this example). My idea (Playground):
trait Foo {
// Note: in my real application, the trait has other methods as well,
// so I can't simply add `Clone` as super trait
fn foo(&self)
where
Self: Clone;
}
struct NoClone;
impl Foo for NoClone {}
Sadly, this leads to:
error[E0046]: not all trait items implemented, missing: `foo`
--> src/lib.rs:8:1
|
2 | / fn foo(&self)
3 | | where
4 | | Self: Clone;
| |____________________- `foo` from trait
...
8 | impl Foo for NoClone {}
| ^^^^^^^^^^^^^^^^^^^^ missing `foo` in implementation
I don't understand this error: the compiler clearly knows that NoClone
does not implement Clone
, so why am I required to provide a definitoin for foo
? In particular, if I attempt to provide a definition (Playground):
impl Foo for NoClone {
fn foo(&self)
where
Self: Clone
{
unreachable!()
}
}
I get the error:
error[E0277]: the trait bound `NoClone: std::clone::Clone` is not satisfied
--> src/lib.rs:9:5
|
9 | / fn foo(&self)
10 | | where
11 | | Self: Clone
12 | | {
13 | | unreachable!()
14 | | }
| |_____^ the trait `std::clone::Clone` is not implemented for `NoClone`
|
= help: see issue #48214
= help: add #![feature(trivial_bounds)] to the crate attributes to enable
So the compiler knows for sure. (FYI: with #![feature(trivial_bounds)]
it compiles, but I don't want to define a bunch of methods with unreachable!()
as body.)
Why does the compiler force me to provide the method definition? Can I work around this problem somehow?
All implementors of a trait need to implement all methods that don't have a default implementation. It's the point of a trait that it has a defined interface. Adding trait bounds to a method does not change anything about this rule.
This is what the language reference says on the topic:
A trait implementation must define all non-default associated items declared by the implemented trait, may redefine default associated items defined by the implemented trait, and cannot define any other items.
This also means that a trait bound on Self
in a method declaration on a trait is functionally equivalent to declaring a supertrait, except that the trait can only be used in the method that declares the bound.
The obvious work-around is to define a separate trait for the methods that have additional requirements on Self
:
trait FooExt: Foo + Clone {
fn foo(&self);
}
You can now implement Foo
for all types, and FooExt
in addition for the types that are Clone
.
Updated as requested in the comments: There is a GitHub issue discussing whether it should be allowed to implement methods with unsatisfiable trait bounds without the method body, so at least the { unimplemted()! }
part could be dropped. As of April 2019, this discussion has not come to any conclusion yet, and not even the exact syntax for implementing the uncallable methods has been settled.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With