Confusion arround how obligations are checked by type system
⚓ Rust 📅 2025-07-28 👤 surdeus 👁️ 12I am a little confused about how obligations (as defined in the compiler dev book) are handled by the type checker.
In my naive understanding, when we include a where clause, we are adding obligations. The type checker will then attempt to make sure that these obligations can be fulfilled.
Further, when writing a impl block, we can write blocks that are made non-overlapping via where clauses. For instance, this compiles just fine in the playground (also on nightly).
trait MarkerTraitA{}
trait MarkerTraitB{}
trait Takes <T>{
fn take(&self, val : T);
}
struct Message<T>(T);
struct Wrapper<T>{
val: T
}
impl<T> Takes<T> for Wrapper<T> where
Message<T> : MarkerTraitA {
fn take(&self, val : T){
return;
}
}
impl<T> Takes<T> for Wrapper<T> where
Message<T> : MarkerTraitB {
fn take(&self, val : T){
return;
}
}
fn main(){
return;
}
Though, I am a little confused that this compiles correctly, as I have specified bounds that should not be able to be satisfied given that we are bounding with a local type and trait (and therefore, by the orphan rule, we have the complete set of implementations, no upstream or downstream create can ever add more).
With one added line, we can make this not compile:
trait MarkerTraitA{}
trait MarkerTraitB{}
trait Takes <T>{
fn take(&self, val : T);
}
struct Message<T>(T);
impl<T> MarkerTraitA for Message<T>{} // this is the added line here
struct Wrapper<T>{
val: T
}
impl<T> Takes<T> for Wrapper<T> where
Message<T> : MarkerTraitA {
fn take(&self, val : T){
return;
}
}
impl<T> Takes<T> for Wrapper<T> where
Message<T> : MarkerTraitB {
fn take(&self, val : T){
return;
}
}
fn main(){
return;
}
This throws the error:
error[E0283]: type annotations needed: cannot satisfy `Wrapper<T>: Takes<T>`
--> src/main.rs:25:22
|
25 | impl<T> Takes<T> for Wrapper<T> where
| ^^^^^^^^^^
|
note: multiple `impl`s satisfying `Wrapper<T>: Takes<T>` found
--> src/main.rs:18:1
|
18 | / impl<T> Takes<T> for Wrapper<T> where
19 | | Message<T> : MarkerTraitA {
| |_________________________^
...
25 | / impl<T> Takes<T> for Wrapper<T> where
26 | | Message<T> : MarkerTraitB {
| |_________________________^
The use of E0283 is rather confusing to me, as this error seems to be a type inference error, whereas I would expect the "true" error to be that there does not exists an implementation that satisfies the bounds, but maybe I am barking up the wrong tree with this idea that where clause bounds have to be able to be satisfied. Looks like RFC2056 allows for "trivial where clause bounds", but I can't seem to track down why in the first case this compiles but it stumbles on the second.
Thanks for reading, and I apologize in advance if I am missing something rather obvious here.
1 post - 1 participant
🏷️ Rust_feed