`&mut T` to `&T` coercion

Hi, there
consider the example:

fn foo(x: &[&i32]) {}

fn main() {
    let mut a = vec![1i32,2,3,4,5];
    let b: Vec<_> = a.iter_mut().collect();
    foo(b.as_slice())
}

It fails with the error:

error[E0308]: mismatched types
 --> src/main.rs:6:9
  |
6 |     foo(b.as_slice())
  |         ^^^^^^^^^^^^ types differ in mutability
  |
  = note: expected type `&[&i32]`
             found type `&[&mut i32]`

Can anyone explain the fundamental reason why it cannot be coerced automatically by compiler?

1 Like

Because you can’t coerce types inside of other types. i.e. the &mut i32 is inside of &[_], so you can’t coerce it to &i32. This sort of coercion doesn’t work in general so it is not allowed.

1 Like

For an example where it’s more obvious that this doesn’t work in general, consider coercing &[&&i32] to &[&i32].

1 Like

Another example of this not working in general: one fundamentally cannot &[&String] -> &[&str] in O(1) because those inner references are not layout-compatible (the latter being a fat pointer).

1 Like

It doesn’t work in general, but &mut T and &T are layout compatible. The only reason this coercion doesn’t happen is because the compiler doesn’t allow it.

I guess that it would very problematic to determine on generic level, what is actually possible for coercion or what has different layout. There would be exceptions to exceptions, and after all very big mess. If you are using “embeded borrows”, just use Borrow/AsRef traits which are designed for such cases:

use std::borrow::Borrow;

fn foo(x: &[impl Borrow<i32>]) {}

fn main() {
    let mut a = vec![1i32,2,3,4,5];
    let b: Vec<_> = a.iter_mut().collect();
    foo(b.as_slice())
}
2 Likes

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