Vraag Wat zijn niet-lexicale levens?


Rust heeft een RFC gerelateerd aan niet-lexicale levens die is goedgekeurd om lang in de taal te worden geïmplementeerd. Kort geleden, Rust's ondersteuning van deze functie is veel verbeterd en wordt als voltooid beschouwd.

Mijn vraag is: wat is precies een niet-lexicale levensduur?


16
2018-05-09 10:45


oorsprong


antwoorden:


Het is het gemakkelijkst om te begrijpen wat niet-lexicale levens zijn door te begrijpen wat lexicale levens zijn. In versies van Rust voordat niet-lexicale levens aanwezig zijn, zal deze code mislukken:

fn main() {
    let mut scores = vec![1, 2, 3];
    let score = &scores[0];
    scores.push(4);
}

De Rust-compiler ziet dat scores is geleend door de score variabele, dus het staat verdere mutatie van scores:

error[E0502]: cannot borrow `scores` as mutable because it is also borrowed as immutable
 --> src/main.rs:4:5
  |
3 |     let score = &scores[0];
  |                  ------ immutable borrow occurs here
4 |     scores.push(4);
  |     ^^^^^^ mutable borrow occurs here
5 | }
  | - immutable borrow ends here

Een mens kan echter triviaal zien dat dit voorbeeld overdreven conservatief is: score is nooit gebruikt! Het probleem is dat het lenen van scores door score is lexicale - het duurt tot het einde van het blok waarin het zich bevindt:

fn main() {
    let mut scores = vec![1, 2, 3]; //
    let score = &scores[0];         //
    scores.push(4);                 //
                                    // <-- score stops borrowing here
}

Niet-lexicale levens herstellen dit door de compiler te verbeteren om dit detailniveau te begrijpen. De compiler kan nu nauwkeuriger bepalen wanneer een leen nodig is en deze code zal compileren.

Het mooie aan niet-lexicale levens is dat wanneer het eenmaal is ingeschakeld, niemand zal er ooit aan denken. Het wordt gewoon "wat Rust doet" en dingen zullen (hopelijk) gewoon werken.

Waarom waren lexicale levens toegestaan?

Rust is bedoeld om alleen bekende veilige programma's te compileren. Echter, het is onmogelijk precies toelaten enkel en alleen veilige programma's en verwerp onveilige programma's. Daarom vergaat roest aan de kant van conservatief: sommige veilige programma's worden afgewezen. Lexicale levensduur is hiervan een voorbeeld.

Lexicale levens waren veel eenvoudiger te implementeren in de compiler, omdat kennis van blokken "triviaal" is, terwijl kennis van de gegevensstroom minder is. De compiler moest zijn herschreven om een ​​"mid-level intermediate representation" (MIR) in te voeren en te gebruiken. Vervolgens moest de leencontrole (ook bekend als "borrowck") herschreven worden om MIR te gebruiken in plaats van de abstracte syntaxboom (AST). Vervolgens moesten de regels van de leencontroleur worden verfijnd om fijner te worden.

Lexicale levens staan ​​de programmeur niet altijd in de weg, en er zijn veel manieren om lexicale levens te werken wanneer ze dat doen, zelfs als ze vervelend zijn. In veel gevallen betrof dit het toevoegen van extra accolades of een booleaanse waarde. Hierdoor kon Rust 1.0 worden verzonden en vele jaren bruikbaar zijn voordat niet-lexicale levens werden geïmplementeerd.

Interessant, zeker goed patronen werden ontwikkeld vanwege lexicale levens. Het beste voorbeeld voor mij is de entry patroon. Deze code mislukt vóór niet-lexicale levens en compileert ermee:

fn example(mut map: HashMap<i32, i32>, key: i32) {
    match map.get_mut(&key) {
        Some(value) => *value += 1,
        None => {
            map.insert(key, 1);
        }
    }
}

Deze code is echter inefficiënt omdat deze de hash van de sleutel tweemaal berekent. De oplossing die is gemaakt omdat van lexicale levens is korter en efficiënter:

fn example(mut map: HashMap<i32, i32>, key: i32) {
    *map.entry(key).or_insert(0) += 1;
}

De naam "niet-lexicale levens" klinkt niet goed voor mij

De levensduur van een waarde is de periode waarin de waarde op een specifiek geheugenadres blijft (zie Waarom kan ik een waarde en een verwijzing naar die waarde niet in dezelfde structuur opslaan? voor een langere uitleg). De functie die bekend staat als niet-lexicale levensduur is dat niet verandering de levens van alle waarden, dus het kan niet-lexical levens maken. Het maakt het volgen en controleren van lenen van die waarden alleen nauwkeuriger.

Een meer nauwkeurige naam voor de functie kan "niet-lexicaal" zijn leentSommige compiler-ontwikkelaars verwijzen naar de onderliggende "op MIR gebaseerde lening".

Niet-lexicale levens waren nooit bedoeld als een "gebruikersgerichte" functie, per se. Ze zijn meestal groot geworden in onze gedachten vanwege de kleine papieruithalingen die we krijgen door hun afwezigheid. Hun naam was vooral bedoeld voor interne ontwikkelingsdoelen en het veranderen ervan voor marketingdoeleinden was nooit een prioriteit.

Ja, maar hoe gebruik ik het?

U niet doen in een stabiele roest. Op een gegeven moment wordt iedereen ingeschakeld en zal er niets zijn wat je moet doen.

In de nachtversies van Rust kun je je via een functie aanmelden voor NLL:

#![feature(nll)]

Je kunt zelfs kiezen voor de experimentele versie van NLL met de compilervlag -Z polonius.

Een voorbeeld van echte problemen opgelost door niet-lexicale levens


20
2018-05-09 12:33