Description
openedon Sep 13, 2021
pub trait Marker<'a> {}
pub trait Trait where for<'a> &'a Self: Marker<'a> {
fn usable(&self) {}
}
impl<'a> Marker<'a> for &'a () {}
impl Trait for () {}
pub fn f() {
// `(): Trait`
let u = ();
u.usable();
// `dyn Trait` exists and `()` can coerce to it...
let _t: &dyn Trait = &();
// But `dyn Trait: !Trait`
// _t.usable();
// error[E0277]: the trait bound `for<'a> &'a T: Marker<'a>` is not satisfied
}
I expected to see this happen: The unsized coercion is denied (but see discussion below).
Instead, this happened: The unsized coercion happened and we end up with a dyn Trait
which does not implement Trait
.
Meta
This works on any stable version of Rust.
Notes
The failure to meet the trait bound is caught when you actually try to use the dyn Trait
, at least trivially. I haven't found a way to weaponize this, but I haven't put any effort into doing so either.
dyn Trait
implements Trait
if you impl<'a> Marker<'a> for &'a (dyn Trait + 'static) {}
in the example. However, the non-super-trait bound need not be a marker trait (it can have non-defaulted methods).
Related: Implied Bounds, #20671
Discussion
I'm filing this as a bug because the assertion that dyn Trait: Trait
always holds has been expressed as a belief by Rust team members, e.g.
There are some possibilities in line with this assertion:
- Unsized coercion checks the bounds and is denied in this case
- Bounds that aren't super-traits make a trait object unsafe generally
- Bounds that aren't super-traits don't apply to
dyn Trait
However, another possibility is to generalize RFC 2027 and just accept the current behavior:
dyn Trait
is always a valid type, as per RFC 2027- Unsized coercion is still dependent on all methods being either object safe or not applicable (
where Self: Sized
)- Though this could be relaxed too (if you're not object safe,
dyn Trait
just doesn't implementTrait
)
- Though this could be relaxed too (if you're not object safe,
- If unsized coercion is possible, you can instantiate a
dyn Trait
, even if it doesn't implementTrait
- Naturally, you can't use the
Trait
methods if it doesn't implementTrait
I'm sure there are a lot of subtleties and backwards-compatibility concerns in any of the possible approaches.