coercion: use a generalization of the target to specialize the source to coerce instead of propagating the wrong type. by eddyb · Pull Request #27292 · rust-lang/rust (original) (raw)
The issue solved here involved generic "constructor" traits and unsizing coercions.
A real usecase is box
desugaring, which is being demonstrated as a test-case.
Given such a trait with more than one implementation, e.g. for Box<T>
and Rc<T>
:
trait Make { fn make(T) -> Self; } impl Make for Box { fn make(x: T) -> Box { Box::new(x) } } impl Make for Rc { fn make(x: T) -> Rc { Rc::new(x) } }
An direct call would be too unconstrained for a coercion, so the expected type ends up propagated:
fn make_boxed_trait<E: Error>(x: E) -> Box { // error: Make not implemented for Box. Make::make(x) }
The workaround was to manually specify enough about the produced type to allow resolving it:
fn make_boxed_trait<E: Error>(x: T) -> Box { let boxed: Box<_> = Make::make(x); // <Box<_> as Make> gets selected to the appropriate impl, and so // boxed has the type Box at this point, which can coerce to Box. boxed }
That workaround is implemented in this PR, for all coercions where unsizing was ambiguous.
A generalized type is picked based on the CoerceUnsize
impl that could be used for coercing.
For example, CoerceUnsized<Box<Error>>
is implemented by Box<T> forall T
, so the generalized
type is Box<_>
, which matches both Box<E>
and Box<Error>
.
Such generalization allows constraining the source of the potential coercion while still allowing
it to be any type that could coerce to the target, or the target type itself.