How to create a int-slice that filters elements greater than X of the underlying array?

I'd like to create a slice that only accesses elements up to x, where x is some element in the underlying vector. We create slices by index, not value. The Rust book, slice chapter, does something similar to what I'd like in an example:

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    &s[..]
}

Which I've adapted to throw into main,

    for (i, &item) in v_int[..200].iter().enumerate() {
        if item > 1000 {
            let v_lt1000 = &v_int[0..i];
            break;
        }
    }

Though obviously, if I'd like to use v_int later I'm out of luck, since v_lt1000 will go out of scope. I could solve this by just throwing everything in a function, but suppose I'm stubborn and want to do it in main. How do I create that slice?

PS. This is my first time posting here. I read the Community Guidelines, but if I did something wrong, yell at me.

Post edit,
I considered using some functional tools. The following does what I'd like it to.

    let v_temp = &v_int[..200]; // 200 an estimate index, just to shorten computation
    let vlt = v_temp
        .iter()
        .filter(|&&x| x < 1000)
        .collect::<Vec<&u32>>();

If someone has another solution, I'd still be interested to know it.

Is your input (v_int) in sorted order?
In that case, you could do:

let vlt = v_int()
     .iter()
     .copied()
     .take_while(|x| x < 1000)
     .collect::<Vec<_>>();

That would be the way I would do it for copy types like a u32.
You could adapt your original solution like so:

    let v_lt1000;
    for (i, &item) in v_int[..200].iter().enumerate() {
        if item > 1000 {
            v_lt1000 = &v_int[0..i];
            break;
        }
    }

But that would be substantially less rusty, and they would likely to compile to (more or less) the exact same code.

2 Likes

How about

let idx = v_int.iter().position(|item| *item > 1000).expect("Not found");
let v_lt1000 = &v_int[..idx];
2 Likes

A couple modifications;
it seems you've made a few errors. This playground corrects them.
Two quick questions;
Am I correctly interpreting that your soln, using copied and take_while does NOT clone the original but references (by copy) the (immutable) data?
And the follow up, is that doing the same thing I'm doing with filter, but with a bit more explicitness that we're exploiting the copy trait?

The latter solution does exactly what I was thinking to do after reading the book! I don't think I realized I could use a let like that. But yeah, it doesn't look great still, I'm a fan of either your first suggestion or the thing I wrote.

Cool, that works too, and you taught me that the .position iterator function exists.
On top of that, (I think) it's the most readable of the 4 ways I think we came up with. Thanks!

1 Like

In this particular case, it does not clone, but it does, well, copy, but u32's are just as cheap to copy as references (possibly more efficient if you are copying enough of them, actually), since its they are just simple memcopys or register moves.

Also yes, its doing the same thing your example does, but replacing the filter with a take while so you dont need the [..200] 'optimization]', and it uses the copy trait since there really is (generally) no reason to have a vector of &u32. Especially on a 64 bit system where a Vec<&u32> takes up twice the space as the same length Vec<u32>

This solution does allocate, but what I would do to avoid the allocation in actual code would be to just add my logic onto the end of the iterator chain instead of .collect()ing it.

If you do really and truly need an actual borrowed slice, alice's solution is the one to go with.

@thatonelutenist I think I must be a bit confused.

Some "facts":

  1. v_int owns some data, of type vec<u32>, where u32 implements copy.
  2. In my solution, v_lt1000 is a slice over v_int, as it is constructed from the slice v_temp
  3. in your solution, vlt is a slice over v_int, and is more explicit about copying, where I hide my copying with an intermediate let v_temp = &v_int[..200];
  4. In @alice's solution, she constructs a slice over v_int
  5. All of these slices contain the same data: they are slices: slices are a) a reference to a piece of data in a collection, and b) a capacity

You wrote that I'm using a borrowed slice which contains a whole array of references. This contradicts 5. You also claim that one of either 4 or (both 2 and 3) are wrong.

In my solution, vlt is not a slice, its a new owned vector (whenever you see .collect() you are constructing a new owned something).

In your iterator solution, that owned vector is a vector of references, which is a little inefficient for integer types.

In alice's solution (and your original one), v_lt1000 is a borrowed slice, which doesn't actually copy anything, since slices are, at their core, just a pointer with some length information.

1 Like

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