Removing `unwrap` for largest subslice?

Hello, does anyway know a pretty way to do the following?

    let len = a.len().min(b.len());
    let dst = b.get_mut(..len).unwrap();
    let src = a.get(..len).unwrap();
    use(dst, srt);

Maybe you prefer indexing? It does the panicking implicitly:

fn main() {
    let a: String = String::new();
    let mut b: Vec<u8> = vec![];

    let len = a.len().min(b.len());

    let dst = &mut b[..len];
    let src = &a[..len].as_bytes();
    
    r#use(dst, src);
}

fn r#use(_: &mut [u8], _: &[u8]) {}

Playground.

1 Like

It still contains panic paths, while I would like to not have them at all

Note that the optimizer is able to remove both panic paths Compiler Explorer

2 Likes

In cases where the compiler can't optimize these, you can use unchecked methods.

Yes, but this is not guaranteed by type system and generally is not pretty.

It is like using

if option.is_some() {
  let value = option.unwrap();
  ...
}

instead of

if let Some(value) = option {
    ...
}
1 Like

Assuming you can change the argument type of use: have it take an iterator over pairs:

use(b.iter().zip(a.iter()))
2 Likes

Having things guaranteed by the type system is nice, but for many advanced patterns it's not really feasible outside of languages with dependent types (where your code is effectively a mathematical proof that it's correct)

3 Likes

Rust's type system can't reason about run-time sized types like slices. It might be possible for arrays, but const generics aren't powerful enough for this yet on stable.

For unwraps that you know can't fail, you could use .unwrap_or_default() that usually generates easier to optimize code, leaves less code bloat in cases where it can't be optimized out.

Define a helper function and use that. The code using the function won't need unwrap or indexing:

/// This function never panics.
fn equal_length_prefixes<'a, 'b, T>(a: &'a [T], b: &'b [T]) -> (&'a [T], &'b [T]) {
    let len = a.len().min(b.len());
    (&a[..len], &b[..len])
}

This is probably too clever, but there's no panic path:

let src = match a.get(..b.len()) {
    Some(src) => src,
    None => &a,
};
let dst = match b.get_mut(..a.len()) {
    Some(dst) => dst,
    None => &mut b,
};
9 Likes

EDIT: The compiler error below is fixed with the next version of the borrow checker, polonius, so it isn't directly related to this topic.

I like it! Although I wasn't able to use this to write a function that returns the slices this way:

fn prefixes<'a, 'b, T>(a: &'a [T], b: &'b mut [T]) -> (&'a [T], &'b mut [T]) {
    let src = match a.get(..b.len()) {
        Some(src) => src,
        None => a,
    };
    let dst = match b.get_mut(..a.len()) {
        Some(dst) => dst,
        None => b,
    };
    (src, dst)
}
error[E0499]: cannot borrow `*b` as mutable more than once at a time
  --> src/lib.rs:17:25
   |
10 |         fn prefixes<'a, 'b, T>(a: &'a [T], b: &'b mut [T]) -> (&'a [T], &'b mut [T]) {
   |                         -- lifetime `'b` defined here
...
15 |             let dst = match b.get_mut(..a.len()) {
   |                             - first mutable borrow occurs here
16 |                 Some(dst) => dst,
17 |                 None => b,
   |                         ^ second mutable borrow occurs here
18 |             };
19 |             (src, dst)
   |             ---------- returning this value requires that `*b` is borrowed for `'b`

However, slicing does work:

fn prefixes<'a, 'b, T>(a: &'a [T], b: &'b mut [T]) -> (&'a [T], &'b mut [T]) {
    let len = a.len().min(b.len());
    (&a[..len], &mut b[..len])
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.