While playing with workarounds for this issue , I found this problem (playground ):
trait T {}
trait H {
type Inner: T;
}
fn assert_t<A: T>() {}
fn does_not_work<A: H<Inner=B>, B>() {
assert_t::<A::Inner>();
}
This fails with this:
error[E0277]: the trait bound `B: T` is not satisfied
--> src/lib.rs:10:16
|
10 | assert_t::<A::Inner>();
| ^^^^^^^^ the trait `T` is not implemented for `B`
|
note: required by a bound in `assert_t`
--> src/lib.rs:7:16
|
7 | fn assert_t<A: T>() {}
| ^ required by this bound in `assert_t`
help: consider restricting type parameter `B`
|
9 | fn does_not_work<A: H<Inner=B>, B: T>() {
| +++
It seems like the compiler sees A::Inner
as B
, but forgets additional constraints on A:Inner
. Is this expected behavior? Removing the equality Inner=B
makes the code compile.
I also tried doing the same with IntoIterator
and for loops to see if something fun would happen if a type would implement IntoIterator
, but the inner type would not implement Iterator
, but I got basically the same error as above. The same thing is true for futures and await.
Here's an issue about it.
opened 10:02PM - 01 Apr 23 UTC
A-trait-system
T-compiler
C-bug
Apologies if this has been covered elsewhere, I wasn't able to find any issues o… r anything in the RFCs that addresses this specifically.
## Issue
Given a trait with an associated type that has some trait bounds:
```rust
trait Foo<T> {
type X: Clone;
}
// An implementation, as an example:
impl<T: Clone> Foo<T> for () { type X = T; }
```
We can use this trait's associated type `X` (without constraining `X` to a particular type or adding bounds to it) while relying on `X: Clone`:
```rust
fn example4<A, Witness: Foo<A>>(a: &<Witness as Foo<A>>::X) -> <Witness as Foo<A>>::X {
a.clone()
}
```
However if we are to bound `X` to a particular type (i.e. `Foo<_, X = A>`) we are no longer able to rely on `X: Clone`:
```rust
fn example1<A, Witness: Foo<A, X = A>>(a: &A) -> A {
a.clone() // errors
}
// Note that the issue is not that `rustc` cannot unify `A` with
// `<Witness as Foo<A, X = A>>::X`; the following fail in the same way (and the
// error messages in all of these name `A` specifically):
//
// (this also be verified using something like `fn same_ty<A>(_: &A, _: &A) {}`,
// bounding both types with `Default` and passing `&<_ as Default>::default()`
// in, etc.)
fn example2<A, Witness: Foo<A, X = A>>(a: &<Witness as Foo<A>>::X) -> A {
a.clone() // errors
}
fn example3<A, Witness: Foo<A, X = A>>(a: &<Witness as Foo<A>>::X) -> <Witness as Foo<A>>::X {
a.clone() // errors
}
// Note that the only difference between ^ and `example4` is that ^ has `X = A`
// in its bound on `Witness`.
```
Note that for all of the above, `A` is required to be `Clone` (when `Witness = ()`) in order for _uses_ of the functions (or of `(): Foo<A, X = A>`) to typecheck:
```rust
// All of these fail to compile as you'd expect:
let _ = example1::<NotClone, ()>;
let _ = example2::<NotClone, ()>;
let _ = example3::<NotClone, ()>;
```
([playground link](https://play.integer32.com/?version=stable&mode=debug&edition=2021&gist=a86ee6bbad38affaab221c616d8d230c))
---
(this particular example is minified from something less contrived; as written this _is_ fairly nonsensical since there's not much reason to ask for `Witness: Foo<X = T>` instead of just bounding `T` directly)
## Misc
Prior to Rust 1.49 (I have not bisected to verify but I think the change traces back to #72788 and #73905), functions like `example{1,2,3}` were required to bound `A: Clone` themselves so that `Foo<A, X = A>` would be well-formed ([godbolt link](https://rust4.godbolt.org/z/s1MEsefa3)). In 1.49 and up this check seems to be deferred to usages of the function.
This also observable in other ways; for example:
```rust
struct NotClone;
// is the concrete type we specify for the associated type checked for the
// bounds on `X`?
fn example5<W: Foo<NotClone, X = NotClone>>() { }
// it seems not! (in 1.49 and up; in 1.48 and below this does error)
```
I'm not sure if the intent of the change was merely to shift requirements for associated types to callers of functions and to _still_ require authors of such functions to have to spell out bounds like `A: Clone` explicitly _OR_ if the intent actually was to have `A: Clone` be implied.
I was expecting (or really, hoping for) the latter in which case I'd expect to be able to rely on `A: Clone` within the function bodies above.
In either case I think `example3` is particularly surprising; if this really is intended behavior I think it'd be nice to have the compiler communicate why things like `<Witness as Foo<A>>::X` don't have their trait bounds from `Witness` available when `A` is a ty param on a function (or maybe just for this to be documented?).
---
Edit: IIUC [this comment](https://github.com/rust-lang/rust/issues/78893#issuecomment-1199362452) suggests that this is intended behavior. It does still seem unintuitive/suboptimal that as a consequence, the bound (`A: Clone`) is required for these functions to typecheck but that the bound is _not_ available for the function body to rely on.
This issue is might just a duplicate of #78893 (the traits/`impl Trait` made me initially believe it wasn't but that's a bit of a red herring I think).
And they concluded it was probably intentional at the end of the OP, and the comment they link 80%? reads that way to me too, but the team hasn't actually chimed in on the issue directly.
Thank you for finding a previous "discussion"! I tried doing it myself, but github search is not good (this query doesn't find the issue you linked for some reason)
system
Closed
April 18, 2025, 9:47am
5
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.