# Assume slice length is smaller equal \`isize::MAX\`

**URL:** <https://internals.rust-lang.org/t/assume-slice-length-is-smaller-equal-isize-max/20505>\
**Category:** libs\
**Created:** [March 20, 2024, 8:40am UTC](https://internals.rust-lang.org/t/assume-slice-length-is-smaller-equal-isize-max/20505 "2024-03-20T08:40:24Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![DzenanJupic](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dzenanjupic/32/11948_2.png) [@DzenanJupic](https://internals.rust-lang.org/u/DzenanJupic)\
**Post date:** [March 20, 2024, 8:40am UTC](https://internals.rust-lang.org/t/assume-slice-length-is-smaller-equal-isize-max/20505/1 "2024-03-20T08:40:25Z")

</div>

Currently, the slice `len` method just reads the pointer metadata and returns it:

```rust
pub const fn len(&self) -> usize {
    ptr::metadata(self)
}

```

My understanding is, that the length of slices is limited to at most `isize::MAX`. Slices longer than that would be unsound. Therefore, i.e. adding up the length of two different slices or adding a small number should never overflow/wrap.

Still, when looking at the compiler output for such operations, this guarantee seems to not be picked up: [[godbolt](https://godbolt.org/z/Wv8W5odGK)]

```rust
pub fn add_one<T>(slice: &[T]) -> usize {
    match slice.len().checked_add(1) {
        Some(n) => n,
        None => unreachable!()
    }
}

pub fn add_one_assume<T>(slice: &[T]) -> usize {
    unsafe { assert_unchecked(slice.len() <= isize::MAX as usize) };
    match slice.len().checked_add(1) {
        Some(n) => n,
        None => unreachable!()
    }
}

pub const unsafe fn assert_unchecked(expr: bool) {
    if !expr {
        std::hint::unreachable_unchecked();
    }
}

```

Here, the second function generates panic-free code, as expected, while the first function does not. In practice, this has negative performance implications for i.e. array-vectors, since for a function like `copy_from_slice` the compiler cannot ~~guarantee~~ proof that `self.len() + slice.len() >= self.len()` which leads to unnecessary panicking branches.

A really simple solution would be to just add an `assert_unchecked` statement to the slice `len` function. Would a PR for that be accepted?

---

<div class="post-metadata">

**Author:** ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)\
**Post date:** [March 20, 2024, 9:58am UTC](https://internals.rust-lang.org/t/assume-slice-length-is-smaller-equal-isize-max/20505/2 "2024-03-20T09:58:44Z")

</div>

> [@DzenanJupic](#):
>
> A really simple solution would be to just add an `assert_unchecked` statement to the slice `len` function. Would a PR for that be accepted?

As one detail, only slices with `T` not zero-sized have guaranteed `len` \<= `isize::MAX as usize`. The guarantee is more precisely that `len * size_of::<T>()` \<= `isize::MAX as usize`, so that's an even stronger guarantee in case that `size_of::<T>() > 1`. (I'm not sure if this stronger guarantee needs to be re-written into something of a `len <= calculated_max_len::<T>()` form for LLVM to make full use of it.)

---

<div class="post-metadata">

**Author:** ![the8472](https://avatars.discourse-cdn.com/v4/letter/t/0ea827/32.png) [@the8472](https://internals.rust-lang.org/u/the8472)\
**Post date:** [March 20, 2024, 10:10am UTC](https://internals.rust-lang.org/t/assume-slice-length-is-smaller-equal-isize-max/20505/3 "2024-03-20T10:10:03Z")

</div>

I have adding validity ranges to the `&[T]` metadata on my todo list but it requires changes to the compiler. Adding assumes in the library won't get us the niches but should be easier to implement.

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [March 20, 2024, 3:15pm UTC](https://internals.rust-lang.org/t/assume-slice-length-is-smaller-equal-isize-max/20505/4 "2024-03-20T15:15:40Z")

</div>

Lang conversation about what the actual validity rule for reference-to-slice should be:

> <https://github.com/rust-lang/rust/pull/121965>
>
> The length limit on slices is clearly a safety invariant, and I'd like it to als…o be a validity invariant. With \[function parameter metadata\](https://discourse.llvm.org/t/rfc-metadata-attachments-for-function-arguments/76420?u=scottmcm) making progress in LLVM, I'd really like to be able to use it when \`&\[\_\]\` is passed as a scalar pair, in particular.
> 
> The documentation for references is cagey about what exactly is a validity invariant, so for now just elaborate on the consequences of the existing safety rules on slices -- the length restriction follows from the \`size\_of\_val\` restriction -- as a way to help discourage people from trying to violate them.
> 
> I also made the existing warning stronger, since I'm fairly sure it's already UB to violate at least the "references must be non-null" rule, rather than it just being that it "might be UB in the future".
> 
> cc @joshlf @RalfJung

---

<div class="post-metadata">

**Author:** ![afetisov](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/afetisov/32/8508_2.png) [@afetisov](https://internals.rust-lang.org/u/afetisov)\
**Post date:** [March 20, 2024, 4:31pm UTC](https://internals.rust-lang.org/t/assume-slice-length-is-smaller-equal-isize-max/20505/5 "2024-03-20T16:31:59Z")

</div>

Is it actually _always_ true? That's true for 64-bit systems, probably for 32-bit ones. But what about 16-bit, or any other possible bitness? I would assume that such users would not be happy to slash half of the possible address space.

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [March 20, 2024, 4:50pm UTC](https://internals.rust-lang.org/t/assume-slice-length-is-smaller-equal-isize-max/20505/6 "2024-03-20T16:50:51Z")

</div>

They can use the full address space, but not in a single object. Pointer offsets are signed, all the way down to LLVM `getelementptr`, so must not exceed `isize::MAX`.

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [March 20, 2024, 5:04pm UTC](https://internals.rust-lang.org/t/assume-slice-length-is-smaller-equal-isize-max/20505/7 "2024-03-20T17:04:31Z")

</div>

> [@afetisov](#):
>
> But what about 16-bit, or any other possible bitness?

So long as we're using LLVM and interoping with C, it'll be the rule regardless of pointer size. So we've just adopted it into Rust as a fundamental restriction on objects.

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [June 18, 2024, 5:04pm UTC](https://internals.rust-lang.org/t/assume-slice-length-is-smaller-equal-isize-max/20505/8 "2024-06-18T17:04:38Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
