2

I have this:

let stuff = vec![1, 2, 3];
let tail = stuff[1..].iter().collect::<Vec<_>>();
println!("Hello, world! {:?}", tail);

I was wondering if this is idiomatic, as I find the call to iter and then to collect verbose.

pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
Finlay Weber
  • 2,989
  • 3
  • 17
  • 37
  • what wrong with "stuff[1..]" ?!? your question is very unclear – Stargateur May 16 '20 at 05:01
  • @Stargateur the problem is not with "stuff[1..]" but first having to do that, then call iter and then collect. I would have expected there will be a method on Vec that allows to get the tail...for example stuff.tail() or stuff.take(1,2) – Finlay Weber May 16 '20 at 08:30
  • 2
    In the provided code, you're creating a vector of references into the original vector. Is that really what you want? It's pretty niche, so I wouldn't expect a dedicated method. – SCappella May 16 '20 at 08:47
  • @sca That initially escaped me, but your assessment [is correct](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7edb4c982c406875c14f8afb4cb0645e). With that I cannot think of a reason why you would want this. It has the same lifetime constraints as a slice reference would have, but with an additional heap allocation. – IInspectable May 16 '20 at 09:26

3 Answers3

3

You don't need to actually create a new vector to print the values. Just use a slice.

let tail = &stuff[1..];
println!("Hello, world! {:?}", tail);

playground

pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
apatniv
  • 1,771
  • 10
  • 13
  • the assumption is that you have a vector already created you need to work with – Finlay Weber May 16 '20 at 08:28
  • @Finlay In this example `stuff` may be a vector. The point is that you don't need to create a second one. – trent May 16 '20 at 11:14
  • @trentcl but the act of doing &stuff[1..] on a vector turns it into a slice! What if down the line you still wants to operate with a Vector? – Finlay Weber May 16 '20 at 15:27
  • @Finlay A vector of *what*? The code you give in the question collects a vector of references, which is in most respects less useful than just taking a slice. If you want a vector of cloned integers, you can use `to_vec`, as I mentioned in [my answer to another question](https://stackoverflow.com/a/47982727/3650362). – trent May 16 '20 at 15:34
  • @trentcl I am just learning Rust so probably I have the wrong mental model. So here is how I see it. I consider Vector a _collection_ type...you know like _List_ say in Java. In this case I have a vector of i32. I consider a slice as a _view_ into another data-structure. So then I am wondering why to I first need to go from a vec, to a slice, back to a vec if I only want to retrieve the tail of the vec... – Finlay Weber May 16 '20 at 16:23
  • @FinlayWeber Can you think of how would you implement a tail function for a list in Java. Either you create a wrapper type that has list interface or create a new list from original list, or write tail function in place where you need it. IMHO, creating a new wrapper type that obeys "List" interface is cumbersome and creating a new list is inefficient. If you have a vector and creating another vector with just one element removed has performance implications. What if that vector size is 2billion or underlying type can't be copied trivially and you want to retain original list. – apatniv May 16 '20 at 16:48
  • 1
    @Finlay `Vec` is not defined recursively, like "a vector is either empty, or a `T` followed by a `Vec`" (you may have seen lists defined like this in other languages). Instead, it's defined as [a contiguous growable array type](https://doc.rust-lang.org/std/vec/struct.Vec.html). The "tail" of a linked list is another linked list, but the "tail" of a vector is just a *part of the vector* -- it is not a vector itself, because it does not own its contents and is not growable. We can call a contiguous "part of a vector" a *slice*, and you can do useful things with it with or without... – trent May 17 '20 at 17:29
  • ... turning it into a whole new vector, but the slice itself is not a vector. – trent May 17 '20 at 17:32
2

You may want to use [T]::split_first, which returns an Option rather than panicking. This means you can choose what to do if stuff is empty.

if let Some((_, tail)) = stuff.split_first() {
    println!("Hello, world! {:?}", tail);
}

let (_, tail) = stuff.split_first().unwrap() would be equivalent to let tail = &stuff[1..] (but with a slightly different panic message).

See also

trent
  • 25,033
  • 7
  • 51
  • 90
0

Since slices (and therefor Vec) implement DoubleEndedIterator, you can take an iterator (1,2,3,4), reverse it (4,3,2,1), take a number of elements (4,3,2) and reverse it again. This might seem strange at first, yet no manual slicing needs to be done and no special cases need to be considered:

fn main() {
    let v = vec![1,2,3,4,5];

    // Will print the last 3 elements in order; "3,4,5"
    for x in v.iter().rev().take(3).rev() {
        println!("{}", x);
    }
}
user2722968
  • 13,636
  • 2
  • 46
  • 67
  • That's a neat trick, but it doesn't appear to be necessary in the OP's case because they are skipping a set number from the start of the iterator rather than taking a set number from the end. – trent May 16 '20 at 13:36