Why not apply deref coercion for trait-bound generics?

Recently ran into this problem when looking into extending the Pattern API for slices.

Essentially the problem is that we can impl Pattern<&[T]> for &[T] and use that in various associated functions like

pub fn starts_with<'a, P: Pattern<&'a [T]>>(&'a self, pattern: P) -> bool

However, that will break existing usage that depends on deref coercion:

error[E0277]: the trait bound `&Vec<{integer}>: Pattern<&[{integer}]>` is not satisfied
    --> library/alloc/benches/slice.rs:98:31
98   |     b.iter(|| vec.starts_with(&match_vec))
     |                   ----------- ^^^^^^^^^^ the trait `Pattern<&[{integer}]>` is not implemented for `&Vec<{integer}>`
     |                   |
     |                   required by a bound introduced by this call

This is quite unfortunate, as it means that we can't use the Pattern API to implement this.

I found one reference to this behavior at Suggest dereference operations when a deref coercion won't work · Issue #39029 · rust-lang/rust · GitHub, which suggests that this was an explicit decision that is unlikely to be reversed.

My thoughts

Why can't we enable this for references? In every case of a reference, the impl just calls out to &Self::Target anyways. For instance, impl Pattern for &String just delegates to &str.

Could negative bounds help here? We could add something like

impl<'p, 'hs, T, V> Pattern<&'hs [T]> for &'p V
    T: PartialEq + 'p,
    V: ops::Deref<Target = [T]>,
    V != T, // otherwise this conflicts with impl for &T

if we can make it possible to have where T != U bounds, that would be awesome! There are many places I've wanted to make e.g.:

struct MyType<T> {

impl<T, U> From<MyType<T>> for MyType<U>
    T != U, // to avoid overlap with T: From<T>

since lifetimes have been a problem for things like this in the past, maybe we can write it where #[ignore_lifetimes] T != U which considers String != u32 but not &'a u32 != &'b u32

If I'm being honest that sounds like a highway to unsound code.

the idea is to guarantee the where bound isn't satisfied anytime lifetimes could possibly be the difference between the impl applying or not, because that way any lifetime mismatch can't cause unsoundness since the code attempting to be unsound wouldn't compile because the where bound isn't satisfied.

After all, code that can't compile isn't unsound.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.