Why are both &[u8] and &[u8; 3] ok in this example?
fn main() {
let x: &[u8] = &[1u8, 2, 3];
println!("{:?}", x);
let y: &[u8; 3] = &[1u8, 2, 3];
println!("{:?}", y);
}
The fact that
&[T; n]can coerce to&[T]is the aspect that makes them tolerable. — Chris Morgan
Why can &[T; n] coerce to &[T]? In what other conditions does this coercion happen?
[T; n] is an array of length n, represented as n adjacent T instances.
&[T; n] is purely a reference to that array, represented as a thin pointer to the data.
[T] is a slice, an unsized type; it can only be used through some form of indirection.
&[T], called a slice, is a sized type. It's a fat pointer, represented as a pointer to the first item and the length of the slice.
Arrays thus have their length known at compile time while slice lengths are a runtime matter. Arrays are second class citizens at present in Rust, as it is not possible to form array generics. There are manual implementations of the various traits for [T; 0], [T; 1], &c., typically up to 32; because of this limitation, slices are much more generally useful. The fact that &[T; n] can coerce to &[T] is the aspect that makes them tolerable.
There is an implementation of fmt::Debug for [T; 3] where T implements Debug, and another for &T where T implements fmt::Debug, and so as u8 implements Debug, &[u8; 3] also does.
Why can
&[T; n]coerce to&[T]? In Rust, when does coercion happen?
It will coerce when it needs to and at no other times. I can think of two cases:
- where something expects a
&[T]and you give it a&[T; n]it will coerce silently; - when you call
x.starts_with(…)on a[T; n]it will observe that there is no such method on[T; n], and so autoref comes into play and it tries&[T; n], which doesn’t help, and then coercion come into play and it tries&[T], which has a method calledstarts_with.
The snippet [1, 2, 3].starts_with(&[1, 2]) demonstrates both.
Why can
&[T; n]coerce to&[T]?
The other answer explains why &[T; n] should coerce to &[T], here I'll explain how the compiler works out that &[T; n] can coerce to &[T].
There are four possible coercions in Rust:
Transitivity.
- If
Tcoerces toUandUcoerces toV, thenTcoerces toV.
- If
Pointer weakening:
- removing mutability:
&mut T→&Tand*mut T→*const T - converting to raw pointer:
&mut T→*mut Tand&T→*const T
- removing mutability:
-
- If
T: Deref<Target = U>, then&Tcoerces to&Uvia thederef()method - (Similarly, if
T: DerefMut, then&mut Tcoerces to&mut Uviaderef_mut())
- If
-
If
Ptris a "pointer type" (e.g.&T,*mut T,Box,Rcetc), andT: Unsize<U>, thenPtr<T>coerces toPtr<U>.The
Unsizetrait is automatically implemented for:[T; n]: Unsize<[T]>T: Unsize<Trait>whereT: Traitstruct Foo<…> { …, field: T }: Unsize< struct Foo<…> { …, field: U }>, provided thatT: Unsize<U>(and some more conditions to make the job easier for the compiler)
(Rust recognizes
Ptr<X>as a "pointer type" if it implementsCoerceUnsized. The actual rule is stated as, “ifT: CoerceUnsized<U>thenTcoerces toU”.)
The reason &[T; n] coerces to &[T] is rule 4: (a) the compiler generates the implementation impl Unsize<[T]> for [T; n] for every [T; n], and (b) the reference &X is a pointer type. Using these, &[T; n] can coerce to &[T].
来源:https://stackoverflow.com/questions/30794235/what-is-the-difference-between-a-slice-and-an-array
