Пытаясь понять, почему какой-то мой код не компилируется, я создал следующий минимальный тест.
Я пытаюсь написать функцию, которая получает что-то вроде &Vec<i32>
и возвращает что-то, что можно преобразовать в итератор через i32.
Вывод не содержит ничего заимствованного из ввода. Я намерен, чтобы выход имел более длительный срок службы, чем вход.
На мой взгляд новичка, похоже, что это должно работать.
fn error_1<'a, I: IntoIterator<Item=&'a i32>>(_: I) -> impl IntoIterator<Item=i32> + 'static {
vec![1]
}
Но когда я проверяю, может ли вывод жить дольше, чем ввод...
fn test_e1() {
let v = vec![3];
let a = error_1(&v);
drop(v); // DROP v BEFORE a.. should be ok!?
}
Я получаю эту ошибку.
error[E0505]: cannot move out of `v` because it is borrowed
--> src/lib.rs:8:10
|
7 | let a = error_1(&v);
| -- borrow of `v` occurs here
8 | drop(v); // DROP v BEFORE a.. should be ok!?
| ^ move out of `v` occurs here
9 | }
| - borrow might be used here, when `a` is dropped and runs the destructor for type `impl IntoIterator<Item = i32>`
Итак, Rust обеспокоен тем, что возможная реализация IntoIterator МОЖЕТ позаимствовать «v»?
Ссылка на игровую площадку - неработающий код
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=41ef3bce0157cc23f824f20eb0651bd9
Я попытался поэкспериментировать с этим дальше...
Что меня абсолютно сбивает с толку, так это то, почему следующий тест компилируется просто отлично... Кажется, у него та же проблема, но rust с удовольствием его скомпилирует.
fn fine_1<'a, I: IntoIterator<Item=i32>>(_: &I) -> impl IntoIterator<Item=i32> + 'static {
vec![1]
}
fn test_f1() {
let v = vec![3];
let a = fine_1(&v);
drop(v); // DROP v BEFORE a.. should be ok!?
}
Ссылка на игровую площадку для измененного, рабочего кода
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7e92549aa4e741c4dd0aae289afcf9db
Может ли кто-нибудь помочь мне объяснить, что не так с первым кодом?
Как я могу указать, что время жизни возвращаемого типа совершенно не связано со временем жизни параметра?
Просто для удовольствия, еще один сломанный пример.. на этот раз возвращающий что-то, что не имеет ничего общего с Vec.. та же ошибка.
fn error_fn<'a, I: IntoIterator<Item=&'a i32>>(_: I) -> impl Fn() + 'static{
|| {}
}
fn test_fn() {
let v = vec![3];
let a = error_fn(&v);
drop(v);
}
Я не ищу обходной путь... Я могу реорганизовать код.
Цель здесь состоит в том, чтобы научиться... Это обнажает пробел в моем понимании жизни...
Что-то, что до недавнего времени я думал, что прибил :D
Стоит отметить, что если я изменю входные данные на конкретный тип, а не на трейт.. `_: &Vec, то все снова компилируется нормально.
КАЖЕТСЯ время жизни на ассоциированном типе черты, что все ломает... Я только не понимаю, почему!??
impl Trait
в позиции возвращаемого типа неявно фиксируются параметры универсального типа. Это означает, что он ограничен их сроком службы. Насколько я знаю, нет возможности отказаться от этого.
Он не фиксирует параметры времени жизни, поэтому вариант без использования 'a
компилируется нормально.
В этом выпуске приведены некоторые подробности.
На nightly этого можно избежать, используя type_alias_impl_trait, так как он фиксирует только то, что явно указано для него:
#![feature(type_alias_impl_trait)]
type Res = impl IntoIterator<Item = i32>;
fn error_1<'a, I: IntoIterator<Item = &'a i32>>(_: I) -> Res {
vec![1]
}
Вы никогда не прибиваете жизни. Кроме, возможно, одного или двух человек во всем мире. Всегда есть какой-нибудь интересный угловой случай. И я говорю это как человек, который проверял реализацию контролера заимствований :)