What it does
If you need to create a &mut [T; N] from a slice, most commonly to pass it to a function that accepts a fixed-size slice as input, the most natural way of creating such a type from slice, &mut chunk[..4].try_into().unwrap();, silently does the wrong thing.
// Valid mutable array reference creation
let out_data: &mut [u8; 4] = chunk[..4].as_mut_array().unwrap(); // since 1.93
let out_data: &mut [u8; 4] = (&mut chunk[..4]).try_into().unwrap();
let out_data = TryInto::<&mut [u8; 4]>::try_into(&mut chunk[..4]).unwrap();
// Incorrect creation of a mutable reference: this clones the chunk and returns
// a mutable reference to the copy. If we modify `out_data` after this point,
// the changes will not reflect back in our original `chunk` slice.
// 🚫🈲⛔❌ - Do not use the following line
let out_data = &mut chunk[..4].try_into().unwrap();
When I ran into this in real-world code, I've been pulling my hair for over an hour trying to debug this and only managed to figure out what's going on with external help.
Advantage
It actually works
Drawbacks
Potential false positives? I cannot imagine this syntax being used to create an intermediate copy intentionally.
Example
What do you think this snippet will print?
fn main() {
let mut data = vec![0; 4];
fill_4_element_slice(&mut data[..4].try_into().unwrap());
println!("{data:?}");
}
fn fill_4_element_slice(chunk: &mut [usize; 4]) {
for i in 0..4 {
chunk[i] = i;
}
}
It looks like it should be 0, 1, 2, 3 but it's not.
Comparison with existing lints
There are no existing lints relevant to this case: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=00c88eddd9ebbe952a13fcdfbc796a03
Additional Context
This issue is so prevalent in SIMD code that safe_unaligned_simd crate added a warning about it to the README. fearless_simd also runs into it in its public API.
Even without explicit SIMD, this pattern often crops up in numerical and multimedia code that uses it to avoid bounds checks in hot loops.
What it does
If you need to create a
&mut [T; N]from a slice, most commonly to pass it to a function that accepts a fixed-size slice as input, the most natural way of creating such a type from slice,&mut chunk[..4].try_into().unwrap();, silently does the wrong thing.When I ran into this in real-world code, I've been pulling my hair for over an hour trying to debug this and only managed to figure out what's going on with external help.
Advantage
It actually works
Drawbacks
Potential false positives? I cannot imagine this syntax being used to create an intermediate copy intentionally.
Example
What do you think this snippet will print?
It looks like it should be
0, 1, 2, 3but it's not.Comparison with existing lints
There are no existing lints relevant to this case: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=00c88eddd9ebbe952a13fcdfbc796a03
Additional Context
This issue is so prevalent in SIMD code that
safe_unaligned_simdcrate added a warning about it to the README.fearless_simdalso runs into it in its public API.Even without explicit SIMD, this pattern often crops up in numerical and multimedia code that uses it to avoid bounds checks in hot loops.