Skip to content

Indexed and IndexedMut traits only accept usize #29010

Closed
@peter-bertok

Description

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

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions