0

I have the following code-snippets (don't question the sense of them ;) )

1. get the n-th element of a Vec recursively

fn helper<T: Clone>(n: usize, current_n: usize, current_xs: &Vec<T>, accumulator: Option<T>) -> Option<T> {
    if current_n > n {
        accumulator
    } else {
        let head = current_xs.get(0).cloned();
        let tail = current_xs.clone().into_iter().skip(1).collect();
        return helper(n, current_n + 1, &tail, head);
    }
}

2. get the length of a Vec recursively

fn helper<T: Clone>(current_xs: &Vec<T>, accumulator: usize) -> usize {
    if current_xs.is_empty() {
        accumulator
    } else {
        let tail = current_xs.clone().into_iter().skip(1).collect();
        return  helper(tail, accumulator + 1)
    }
}

My Question is about that line:

let tail = current_xs.clone().into_iter().skip(1).collect();

In the first example the tail variable is of Type Vec<T> and in the second example the tail variable is of type &Vec<?>.

Questions:

  • Why? Why returns the the exact line of code two different types?
  • How can I return a Vec<T> in the second example?

Rust Playground

sshashank124
  • 31,495
  • 9
  • 67
  • 76
SleepyX667
  • 670
  • 9
  • 21
  • Side note: I know you're just doing this as an exercise but it's still useful to note that (and for future readers), it's more idiomatic to take a slice as an argument instead of an immutable reference to a `Vec` – sshashank124 Jan 21 '20 at 08:15
  • What gives you the impression that these two variables have different types? The second example simply does not compile, and can be fixed by changing `tail` to `&tail` in the call to `helper()`. – Sven Marnach Jan 21 '20 at 08:16
  • @justinas lol... you are right, when I modify the helper call to `return helper(&tail, accumulator + 1)` in IntelliJ the 'type' of tail changes to `Vec`. But I still don't get it, why does the line after `let tail ...` has an effect on that line `let tail...` – SleepyX667 Jan 21 '20 at 08:16
  • @SvenMarnach IntelliJ says it. it resolve the types somehow. – SleepyX667 Jan 21 '20 at 08:18
  • @SleepyX667 can you please create a [Playground link](https://play.rust-lang.org/) with a minimal example so that everyone is on the same page and observes the same things you do – sshashank124 Jan 21 '20 at 08:19
  • @sshashank124 whats a playground link? Do I have to create a github repository? – SleepyX667 Jan 21 '20 at 08:20
  • @SleepyX667 You don't specify a type for `tail`, so its type needs to be inferred by the Rust compiler. The `collect()` method has a generic return type, so it's not useful for inferring the type – you could collect the iterator into any of several types of collections. Hence the conmpiler uses other information it has on `tail` to restrict its type – in this case the fact that it is passed as parameter to `helper()`, so it should have the type of that parameter. – Sven Marnach Jan 21 '20 at 08:20
  • @SleepyX667 https://play.rust-lang.org/ – Sven Marnach Jan 21 '20 at 08:20
  • I recommend trying to _compile_ your code and looking closely at the error messages. In most cases, Rust's error messages are quite useful and give lots of pointers as to what's going on. – Sven Marnach Jan 21 '20 at 08:22
  • @SleepyX667 also I would recommend updating your question title to something more relevant to your question, such as "Collecting to a Vec vs &Vec" or something else that's descriptive – sshashank124 Jan 21 '20 at 09:30

2 Answers2

2

In your second example, collect can return anything that implements FromIterator<Self::Item> (here Self::Item is T). So the compiler tries to guess the type of tail by looking at how it is used. When you call helper (tail, …), the compiler guesses that tail should have the same type as the first argument to helper, aka &Vec<U> for some as yet unknown type U. However, &Vec<U> does not implement FromIterator<T>, so the compiler bails out at this point.

OTOH when you call helper (&tail, …), the compiler guesses that &tail should have type &Vec<U> for some U, and therefore tail should have type Vec<U>. The compiler can then continue and determine that U==T, which gives the complete type of tail as Vec<T>.


As an aside, here is a more idiomatic implementation of your first helper that avoids unnecessary copies. Something similar can be done for the second one:

fn helper<T: Clone> (n: usize, current_n: usize, current_xs: &[T], accumulator: Option<&T>) -> Option<T> 
{
    if current_n > n {
        accumulator.cloned()
    } else {
        let head = current_xs.get (0);
        let tail = &current_xs[1..];
        return if tail.is_empty() { 
            None
        } else {
            helper (n, current_n + 1, tail, head)
        };
    }
}

fn main() {
    let v = vec![1, 2, 3, 4, 5];
    println!("Element 3: {:?}", helper (3, 0, &v, None));
    println!("Element 10: {:?}", helper (10, 0, &v, None));
}

Playground

Jmb
  • 18,893
  • 2
  • 28
  • 55
  • 2
    [here](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=5264f0f8c52821b172777fe0338f956f) is a more idiomatic implementation – Stargateur Jan 21 '20 at 10:20
  • @Stargateur: I got: `error[E0658]: subslice patterns are unstable`, if I don't use nighly channel. – SleepyX667 Jan 21 '20 at 14:16
  • 1
    @SleepyX667 so use [this](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0a077970b6018b1453dd2fcb00cab665), the feature I used just get stabilized in rustc 1.42. This doesn't change much, the point of my code was to reduce the use of useless variable. – Stargateur Jan 21 '20 at 15:26
-1

The problem is:

Example 1: calls the recursive function with

return helper(n, current_n + 1, &tail, head); // &tail

Example 2: calls the recursive function with:

return  helper(tail, accumulator + 1) // tail

Changing tail to &tail everything works. At the moment I can't exactly explain why, so i would wait to accept this as correct answer and hope someone else can completely answer it.

Rust Playground

SleepyX667
  • 670
  • 9
  • 21