Skip to content

Indexed and IndexedMut traits only accept usize #29010

@peter-bertok

Description

@peter-bertok

Sample code: https://play.rust-lang.org/?gist=b9b0fad33060c74095a0&version=nightly

Short sample code that fails to compile:

let a = [0;5];
let x = a[0u8];

I'm working on a native Rust implementation of the Google Brotli compression algorithm (https://github.com/peter-bertok/brotli), which has a lot of low-level bit/byte twiddling and came across this issue.

  • Array indexing using arbitrary integral types works in literally every other mainstream language I can think of. Many other languages allow non-integral types as well, such as their equivalent of repr(C) enums.
  • Many unnecessary explicit type casts are required in certain types of code (binary format parsing, native interop, etc...)
  • An implicit cast from small unsigned integral types is never harmful. That is: u8, u16, and u32.
  • Casts from other types can be handled with a new warning or the existing error, with an explicit cast required to ensure the developer understands that truncation/wrapping is possible.
  • Alternatively, the bounds checking in Rust can be relied upon to provide protection. E.g.: indexing with a negative value should always cause a panic. In fact, this can be made SAFER than an explicit cast. Think of the scenario where a large negative signed number is explicitly cast by the programmer to usize, like they have to do now. This would wrap to a small positive number, which is a valid index. If the rust compiler provides an implementation for Indexed<> with signed types, it can insert a check for negative numbers, improving safety substantially. Currently, this compiles and runs without a panic:
let offs: i64 = -18446744073709551611;
let a = [0;50];
let x = a[offs as usize];
  • This issue also causes the compiler to infer the usize type unexpectedly, which is confusing to developers. Goes against the rule of least surprise. This fails to compile unexpectedly:
let offs: u32 = 1; // explicit integral type. Could be a function parameter, struct member, etc...
let mut i = 0; // due to slice indexing, this is inferred to be `usize`
let a = [0;5]; // any slice of any type, doesn't matter
let x = a[i]; 
// This results in: "the trait `core::ops::Add<u32>` is not implemented for the type `usize`"
let y = a[i+offs]; 

Proposal

  • Implement the Indexed and IndexedMut traits for u8, u16, and u32 for slice and Vec types with identical behaviour to the usize version.
  • Implement the Indexed and IndexedMut traits for isize, i8, i16, and i32 for slice and Vec types with new behaviour that panics on negative values, not just out-of-bounds positive values.
  • The i64 and u64 implementations are problematic on 32-bit builds. How these are treated is up for debate.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    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