Hoe converteer ik een Vec<String> naar Vec<&str>?

Ik kan Vec<String>op deze manier naar Vec<&str>converteren:

let mut items = Vec::<&str>::new();
for item in &another_items {
    items.push(item);
}

Zijn er betere alternatieven?


Antwoord 1, autoriteit 100%

Er zijn nogal wat manieren om dit te doen, sommige hebben nadelen, andere zijn gewoon beter leesbaar voor sommige mensen.

Hiermee wordt de verwijzing naar s(van het type &String) verwijderd naar een String“rechterkantverwijzing”, die vervolgens wordt verwijderd via de eigenschap Derefnaar een str“rechterkantverwijzing” en vervolgens weer omgezet in een &str. Dit is iets dat heel vaak wordt gezien in de compiler, en daarom beschouw ik het als idiomatisch.

let v2: Vec<&str> = v.iter().map(|s| &**s).collect();

Hier wordt de functie Derefvan de eigenschap Derefdoorgegeven aan de functie map. Het is best netjes, maar vereist dat je usede eigenschap gebruikt of het volledige pad geeft.

let v3: Vec<&str> = v.iter().map(std::ops::Deref::deref).collect();

Dit gebruikt dwangsyntaxis.

let v4: Vec<&str> = v.iter().map(|s| s as &str).collect();

Hiermee wordt een segment RangeFullvan de Stringgenomen (slechts een segment in de hele String) en wordt ernaar verwezen. Het is lelijk naar mijn mening.

let v5: Vec<&str> = v.iter().map(|s| &s[..]).collect();

Hiermee wordt gebruik gemaakt van dwang om een ​​&Stringom te zetten in een &str. Kan in de toekomst ook worden vervangen door een s: &str-expressie.

let v6: Vec<&str> = v.iter().map(|s| { let s: &str = s; s }).collect();

Het volgende (bedankt @huon-dbaupp) gebruikt de eigenschap AsRef, die alleen bestaat om eigendomstypen toe te wijzen aan hun respectievelijke geleende type. Er zijn twee manieren om het te gebruiken, en nogmaals, de schoonheid van beide versies is geheel subjectief.

let v7: Vec<&str> = v.iter().map(|s| s.as_ref()).collect();

en

let v8: Vec<&str> = v.iter().map(AsRef::as_ref).collect();

Het komt erop neer dat ik de oplossing v8gebruik, omdat deze het meest expliciet uitdrukt wat je wilt.


Antwoord 2, autoriteit 45%

De andere antwoorden werken gewoon. Ik wil er alleen op wijzen dat als je probeert de Vec<String>om te zetten in een Vec<&str>alleen om het door te geven aan een functie die Vec<&str>als argument, overweeg om de functiehandtekening te herzien als:

fn my_func<T: AsRef<str>>(list: &[T]) { ... }

in plaats van:

fn my_func(list: &Vec<&str>) { ... }

Zoals aangegeven door deze vraag: Functie voor zowel eigen als niet-eigendom stringverzamelingen. Op deze manier werken beide vectoren gewoon zonder dat er conversies nodig zijn.


Antwoord 3, autoriteit 5%

Deze gebruikt collect:

let strs: Vec<&str> = another_items.iter().map(|s| s as &str).collect();

Antwoord 4, autoriteit 5%

another_items.iter().map(|item| item.deref()).collect::<Vec<&str>>()

Om deref()te gebruiken, moet je toevoegen met use std::ops::Deref


Antwoord 5, autoriteit 5%

Alle antwoorden gebruiken idiomatisch iterators en verzamelen in plaats van een lus, maar leggen niet uit waarom dit beter is.

In uw lus maakt u eerst een lege vector en drukt u deze vervolgens in. Rust geeft geen garanties over de strategie die het gebruikt voor groeifactoren, maar ik geloof dat de huidige strategie is dat wanneer de capaciteit wordt overschreden, de vectorcapaciteit wordt verdubbeld. Als de oorspronkelijke vector een lengte van 20 had, zou dat één toewijzing en 5 hertoewijzingen zijn.

Itereren vanuit een vector levert een iterator op met een ‘grootte-hint’. In dit geval implementeert de iterator ExactSizeIteratorzodat het precies weet hoeveel elementen het zal retourneren. mapbehoudt dit en collectprofiteert hiervan door in één keer voldoende ruimte toe te wijzen voor een ExactSizeIterator.

Je kunt dit ook handmatig doen met:

let mut items = Vec::<&str>::with_capacity(another_items.len());
for item in &another_items {
    items.push(item);
}

Hooptoewijzingen en hertoewijzingen zijn waarschijnlijk verreweg het duurste onderdeel van dit hele ding; veel duurder dan het nemen van referenties of schrijven of pushen naar een vector als er geen nieuwe heaptoewijzing is. Het zou me niet verbazen als het in één keer duwen van duizend elementen op een vector die voor die lengte is toegewezen, sneller zou zijn dan het pushen van 5 elementen waarvoor 2 hertoewijzingen en één toewijzing in het proces nodig waren.

Een ander onbezongen voordeel is dat het gebruik van de methoden met collectniet wordt opgeslagen in een veranderlijke variabele die men niet zou moeten gebruiken als het niet nodig is.


Antwoord 6, autoriteit 3%

Hier is een andere optie:

use std::iter::FromIterator;
let v = Vec::from_iter(v.iter().map(String::as_str));

Merk op dat String::as_stris stabiel sinds Rust 1.7.

Other episodes