ideasman42
ideasman42

Reputation: 48068

How to access a slice from pre-defined 'Range' in a struct?

Accessing a slice is straightforward using slice syntax: slice = vector[i..j]

In the case where the range is stored however, from what I can tell you can't do:

struct StructWithRange {
    range: std::ops::Range<usize>,
}

fn test_slice(s: &StructWithRange, vector: &Vec<i32>) {
    let slice = &vector[s.range];
    println!("{:?}", slice); // prints [2, 3]
}

fn main() {
    let vector = vec![1,2,3,4,5];
    let s = StructWithRange {
        range: 1..3
    };

    test_slice(&s, &vector);
}

This gives the error:

error[E0507]: cannot move out of borrowed content
 --> src/main.rs:6:25
  |
6 |     let slice = &vector[s.range];
  |                         ^ cannot move out of borrowed content

Is there a way to get the slice from a range without expanding it?
eg: vector[s.range.start..s.range.end]

If a usize in a struct can be used for an index lookup, why can't a Range<usize> be used in the same way?

Upvotes: 3

Views: 899

Answers (2)

ljedrz
ljedrz

Reputation: 22203

Since Index is a trait requiring the following function:

fn index(&self, index: Idx) -> &Self::Output

It consumes/moves the value used for indexing (index). In your case you are attempting to index the slice using a Range from a borrowed struct, but since you are only passing a reference and the range doesn't implement Copy, this fails.

You can fix it by e.g. changing the definition of test_slice to consume StructWithRange or clone()ing the s.range in the index.

Upvotes: 5

Matthieu M.
Matthieu M.

Reputation: 299950

The error message occurs because Range does not implement Copy and Index consumes its index.

It can be solved by adding a call to .clone(): &vector[s.range.clone()].


If you check the code, it links to the rejected proposal to add Copy to Range in the case where its parameter is Copy here.

The rejection reason is:

These don't have it because they're iterators.

The choice of removing Copy impls instead of adjusted for loop desugaring or linting was made to prevent this problematic case:

let mut iter = 0..n;
for i in iter { if i > 2 { break; } }
iter.collect()

Here iter is actually not mutated, but copied. for i in &mut iter is required to mutate the iterator.

We could switch to linting against using an iterator variable after it was copied by a for loop, but there was no decision towards that.

Upvotes: 4

Related Questions