Skip to content

Lint suggestion: the &mut [T; N] creation pitfall #16507

@Shnatsel

Description

@Shnatsel

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lintArea: New lints

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions