Сбор в Vec vs & Vec
У меня есть следующие фрагменты кода (не сомневайтесь в их смысле;))
1. рекурсивно получить n-й элемент Vec
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<?>
.
Вопросы:
- Зачем? Почему возвращает точную строку кода двух разных типов?
- Как я могу вернуть
Vec<T>
во втором примере?
2 ответа
Во втором примере collect
может вернуть все, что реализует FromIterator<Self::Item>
(Вот Self::Item
является T
). Таким образом, компилятор пытается угадать типtail
глядя на то, как это используется. Когда ты звонишьhelper (tail, …)
, компилятор догадывается, что tail
должен иметь тот же тип, что и первый аргумент для helper
, иначе &Vec<U>
для какого-то еще неизвестного типа U
. Однако,&Vec<U>
никак не реализоватьFromIterator<T>
, поэтому на этом этапе компилятор выходит из строя.
OTOH при звонке helper (&tail, …)
, компилятор догадывается, что &tail
должен иметь тип &Vec<U>
для некоторых 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 = ¤t_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));
}
Проблема в:
Пример 1: вызывает рекурсивную функцию с
return helper(n, current_n + 1, &tail, head); // &tail
Пример 2: вызывает рекурсивную функцию с:
return helper(tail, accumulator + 1) // tail
Изменение tail
к &tail
все работает. На данный момент я не могу точно объяснить, почему, поэтому я бы подождал, чтобы принять это как правильный ответ, и надеюсь, что кто-то еще сможет полностью ответить на него.