2017-01-25 26 views
6

Chciałbym zrobić kodu następujące opracowują:Lifetime adnotacji do argumentu zamknięcia

struct Provider {} 

impl Provider { 
    fn get_string<'a>(&'a self) -> &'a str { "this is a string" } 
} 

fn main() { 
    let provider = Provider{}; 
    let mut vec: Vec<&str> = Vec::new(); 

    // PROBLEM: how do I say that this reference s here 
    // needs to live as long as vec? 
    let fun = |s: &str| { 
     vec.push(s); 
    }; 

    fun(provider.get_string()); 
} 

Playground link

Jest to błąd kompilacji, które otrzymuję:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements 
--> src/main.rs:9:22 
    | 
9 |  let mut vec: Vec<&str> = Vec::new(); 
    |      ^^^^ 
    | 
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the block at 11:24... 
--> src/main.rs:11:25 
    | 
11|  let fun = |s: &str| { 
    |      ^
note: ...so that reference does not outlive borrowed content 
--> src/main.rs:12:18 
    | 
12|   vec.push(s); 
    |    ^
note: but, the lifetime must be valid for the block suffix following statement 2 at 13:6... 
--> src/main.rs:13:7 
    | 
13|  }; 
    |  ^
note: ...so that variable is valid at time of its declaration 
--> src/main.rs:11:9 
    | 
11|  let fun = |s: &str| { 
    |   ^^^ 
+0

Uh! To zabawne. Czasami naprawdę chciałbym móc użyć "dostawcy", by powiedzieć "życie tego faceta, tam". –

+0

Nie wiem, jak to działa. Aby obejść ten problem, być może pozwolisz, aby zamknięcie przechwyciło s także ze środowiska, zamiast przekazywać je jako parametr. Podobnie jak [this] (https://play.rust-lang.org/?gist=e7fd2f4ee7c58b9e592a1f060b2d396b&version=stable&backtrace=0) –

+0

@Shepmaster od tego też znalazłem. To jest rozwiązanie, które działa również na prawdziwy problem :) Wciąż jestem zaskoczony, że nie można ręcznie określić typu 's' tutaj. – bennofs

Odpowiedz

6

Twój kod działa dobrze, jeśli usunąć wszystkie adnotacje całe życie i niech wnioskowanie kompilator wykonywać swoje zadania:

struct Provider; 

impl Provider { 
    fn get_string(&self) -> &str { "this is a string" } 
} 

fn main() { 
    let provider = Provider; 
    let mut vec = Vec::new(); 

    let mut fun = |s| { 
     vec.push(s); 
    }; 

    fun(provider.get_string()); 
} 

Krótko mówiąc, nie ma sposobu, aby wyraźnie odnoszą się do życia zmiennej lokalnej, a jedynie funkcję argumenty. Jednak kompilator wie, jak to zrobić.

Jeśli naprawdę potrzebował, można utworzyć funkcję, aby umożliwić adnotacji wcieleń:

fn thing<'a>(provider: &'a Provider) -> Vec<&'a str> { 
    let mut vec: Vec<&'a str> = Vec::new(); 

    { 
     let mut fun = |s: &'a str| vec.push(s); 

     fun(provider.get_string()); 
    } // End mutable borrow of `vec` 

    vec 
} 

fn main() { 
    let provider = Provider; 
    thing(&provider); 
} 

why did the original annotations stop things from working?

Konkretnie, to ten bit:

let fun = |s: &str| { 
    vec.push(s); 
}; 

Ten deklaruje nowego życia na zamknięcie.Używając składni gotowych (ty can't declare lifetimes on closure arguments), byłoby to równoznaczne z:

let fun = <'a> |s: &'a str| { 
    vec.push(s); 
}; 

Dlatego kompilator ma błąd:

the lifetime cannot outlive the anonymous lifetime #1 defined on [the closure's block]

Nie ma żadnego związku między tym wygenerowanym życia i z Provider. Opuszczenie go umożliwia kompilatorowi wstawienie pożądanego, ale nienazwanego czasu życia.

+0

Czy istnieje jakaś praca/propozycja, która umożliwiłaby wyraźne określenie typu "s" w przyszłej wersji rdzy? t jest pewnego rodzaju specyfikacją okresu życia, która mówi, że odwołanie ma takie samo życie, jak , więc mógłbym napisać '| s: & 'liftime_of (provider) str' – bennofs

+0

@bennofs Nie znam takiego sposobu lub propozycji, aby zezwolić wyraźnie odnosząc się do czasu życia zmiennej lokalnej. – Shepmaster

+0

dzisiaj dowiedziałem się, że wnioskowanie może być potężniejsze (a tylko wygodniejsze) niż ręczne określanie wcieleń ... Dzięki za to! –

2

Oto wersja która kompiluje:

use std::marker::PhantomData; 

struct Provider<'a> { 
    _dummy: PhantomData<&'a()>, 
} 

impl<'a> Provider<'a> { 
    fn get_string(&self) -> &'a str { 
     "this is a string" 
    } 
} 

fn f<'b>() { 
    let provider = Provider { _dummy: PhantomData }; 
    let mut vec: Vec<&str> = Vec::new(); 

    // PROBLEM: how do I say that this reference s here 
    // needs to live as long as vec? 
    let mut fun = |s: &'b str| { vec.push(s); }; 

    fun(provider.get_string()); 
} 

fn main() { 
    f() 
} 

Playground link

Zrobiłem następujące zmiany:

  • Dodać życia do Provider (dodałem PhantomData, ale myślę, że twój dostawca posiada już jakieś dane będzie ona dostarczyć).
  • Zaktualizuj metodę get_string, aby pokazać, że zwraca ona coś z okresem życia dostawcy, a nie wejściowy czas życia (tj. W oparciu o parametr okresu istnienia Provider).
  • Dodaj nowy parametr długości życia 'b do funkcji (której nazwę zmieniono na f(), ponieważ main() nie może jej mieć), której używam do określenia czasu trwania parametru zamknięcia.

Ten ostatni jest nieco mylący, ponieważ najwyraźniej samo dodanie nazwy do życia (bez najwyraźniej dodawania jakichkolwiek ograniczeń) sprawiło, że działa.

Myślę, że (ale chciałbym trochę dokumentacji), że dzieje się tak z powodu wiecznej elizacji. Zamknięcie jest naprawdę ukryte struct z metodą fn call(&self, s: &str) (w tym przypadku). Zgodnie z lifetime elision rules, parametr s zyskuje ten sam czas życia co &self, który jest samym zamknięciem. W takim przypadku zamknięcie jest zadeklarowane po vec, więc czas życia jest zbyt krótki. Wyraźne życie oznacza, że ​​jest ono oddzielone od własnego cyklu życia zamknięcia.

+0

Ta odpowiedź jest z pewnością interesująca i dostarczyła mi kilka nowych informacji na temat życia, ale niestety, w moim problemie, 'Provider' jest' Arena', która nie ma żadnych danych, które posiada :(Więc odniesienie naprawdę już nie żyje niż sam usługodawca: – bennofs

+0

Co decyduje o tym, jak długo w Twoim przypadku występuje 'get_string'? –

+0

To samo dzieje się tak długo, jak" dostawca "ma – bennofs

Powiązane problemy