收集到 Vec vs &Vec

Collecting to a Vec vs &Vec

我有以下代码片段(不要质疑它们的意义;))

1.递归地获取 Vec 的第 n 个元素

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。递归地获取 Vec 的长度

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)
    }
}

我的问题是关于那行的:

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

在第一个示例中,tail 变量的类型为 Vec<T>,在第二个示例中,tail 变量的类型为 &Vec<?>.

问题:

Rust Playground

问题是:

例1:调用递归函数

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

示例 2: 调用递归函数:

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

tail 更改为 &tail 一切正常。 目前我无法准确解释原因,所以我会等待接受这个作为正确答案,并希望其他人能够完全回答它。

Rust Playground

在您的第二个示例中,collect can return anything that implements FromIterator<Self::Item>(这里 Self::ItemT)。所以编译器试图通过查看它的使用方式来猜测 tail 的类型。当您调用 helper (tail, …) 时,编译器猜测 tail 应该与 helper 的第一个参数具有相同的类型,也就是 &Vec<U> 对于一些未知类型 U.但是,&Vec<U> 没有 实现 FromIterator<T>,因此编译器此时退出。

OTOH 当你调用 helper (&tail, …) 时,编译器猜测 &tail 对于某些 U 应该有类型 &Vec<U>,因此 tail 应该有类型Vec<U>。然后编译器可以继续并确定 U==T,它给出 tail 的完整类型为 Vec<T>.


顺便说一句,这是第一个 helper 的更惯用的实现,它避免了不必要的复制。第二个可以做类似的事情:

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