Questa domanda sicuramente è stato risposto prima, ma io non sto chiudendo come un duplicato perché il codice qui è un po 'diverso e penso che sia importante.
Si noti come è stata definita la funzione:
fn combine(orig: &'a str) -> A<'a>
Si dice che restituisca un valore di tipo A
cui interno vivono esattamente fino a quando la stringa fornita. Tuttavia, il corpo della funzione viola questa dichiarazione:
let attr = &*(orig.to_string() + "suffix");
A {
some_attr: attr
}
Qui si costruisce un nuovo String
ottenuto da orig
, prendere una fetta di esso e cercare di tornare dentro A
. Tuttavia, la durata della variabile implicita creata per orig.to_string() + "suffix"
è strettamente inferiore alla durata del parametro di input. Pertanto, il tuo programma è respinto.
Un altro modo più pratico di considerare questo è considerare che la stringa creata da to_string()
e la concatenazione devono vivere da qualche parte. Tuttavia, si restituisce solo una parte presa in prestito di esso. Pertanto, quando la funzione termina, la stringa viene distrutta e la slice restituita non è più valida. Questa è esattamente la situazione che Rust impedisce.
Per superare questo è possibile sia memorizzare una String
all'interno A
:
pub struct A {
some_attr: String
}
oppure è possibile utilizzare std::borrow::Cow
per memorizzare sia una fetta o una stringa di proprietà:
pub struct A<'a> {
some_attr: Cow<'a, str>
}
In quest'ultimo caso vostro funzione potrebbe essere simile a questa:
fn combine(orig: &str) -> A<'static> {
let attr = orig.to_owned() + "suffix";
A {
some_attr: attr.into()
}
}
Si noti che poiché si costruisce la stringa all'interno della funzione, questa viene rappresentata come una variante di proprietà di Cow
e quindi è possibile utilizzare il parametro di durata 'static
per il valore risultante. È possibile anche collegarlo a orig
ma non c'è motivo di farlo.
Con Cow
è anche possibile creare valori di A
direttamente su fette senza allocazioni:
fn new(orig: &str) -> A {
A { some_attr: orig.into() }
}
Qui il parametro durata A
sarà legato (attraverso vita elision) per la durata della stringa di input fetta. In questo caso viene utilizzata la variante presa in prestito di Cow
e non viene eseguita alcuna allocazione.
Si noti inoltre che è meglio usare to_owned()
o into()
per convertire le fette di stringa per String
s perché questi metodi non richiedono codice di formattazione a correre e in modo che siano più efficienti.
come è possibile restituire uno A
di durata 'static
quando lo si sta creando al volo? Non sei sicuro di cosa sia la "variante di proprietà di Cow
" e perché ciò rende possibile 'static
.
Ecco la definizione di Cow
:
pub enum Cow<'a, B> where B: 'a + ToOwned + ?Sized {
Borrowed(&'a B),
Owned(B::Owned),
}
Sembra complesso, ma in realtà è semplice. Un'istanza di Cow
può contenere un riferimento ad un tipo B
o un valore di proprietà che può essere derivato da B
tramite il tratto ToOwned
. Perché str
implementa ToOwned
dove Owned
tipo associato equivale a String
(scritto come ToOwned<Owned = String>
, quando questo enum è specializzato per str
, sembra che questo:
pub enum Cow<'a, str> {
Borrowed(&'a str),
Owned(String)
}
Pertanto, Cow<str>
può rappresentare sia una fetta di stringa o una stringa di proprietà - e mentre Cow
fornisce effettivamente metodi per la funzionalità clone-on-write, è altrettanto spesso utilizzato per contenere un valore che può essere preso in prestito o di proprietà al fine di evitare allocazioni aggiuntive. Perché Cow<'a, B>
implementa Deref<Target = B>
, è possibile ottenere &B
da Cow<'a, B>
con semplice rebo la pubblicazione: se x
è Cow<str>
, quindi &*x
è &str
, indipendentemente da ciò che è contenuto all'interno di x
- naturalmente, è possibile ottenere una porzione di entrambe le varianti di Cow
.
Si può vedere che la variante Cow::Owned
non contiene alcun riferimento al suo interno, solo String
. Pertanto, quando un valore di viene creato utilizzando la variante Owned
, è possibile scegliere la durata desiderata () (ricorda che i parametri di durata sono molto simili ai parametri di tipo generico, in particolare è il chiamante che li sceglie) - ci sono nessuna restrizione su di esso. Quindi ha senso scegliere 'static
come la migliore durata possibile.
orig.to_owned
rimuovere la proprietà da chiunque chiami questa funzione? Sembra che sarebbe scomodo.
Procedimento to_owned()
appartiene ToOwned
caratteristica:
pub trait ToOwned {
type Owned: Borrow<Self>;
fn to_owned(&self) -> Self::Owned;
}
Questa caratteristica viene implementata con str
Owned
pari a String
. Il metodo to_owned()
restituisce una variante di proprietà di qualunque valore venga richiamata. In questo caso specifico, crea String
su &str
, copiando in modo efficace il contenuto della sezione di stringa in una nuova allocazione. Quindi no, to_owned()
non implica il trasferimento della proprietà, è più come se implicasse un clone "intelligente".
Per quanto posso dire String implementa Into<Vec<u8>>
ma non str
, così come possiamo chiamare into()
nel 2 ° esempio?
Il tratto Into
è molto versatile ed è implementato per molti tipi nella libreria standard. Into
viene in genere implementato tramite il tratto From
: se T: From<U>
, quindi U: Into<T>
. Ci sono due implementazioni importanti From
nella libreria standard:
impl<'a> From<&'a str> for Cow<'a, str>
impl<'a> From<String> for Cow<'a, str>
Queste implementazioni sono molto semplici - ma restituisce Cow::Borrowed(value)
se value
è &str
e Cow::Owned(value)
se value
è String
.
Ciò significa che &'a str
e String
attuare Into<Cow<'a, str>>
, e quindi può essere convertito in Cow
con into()
metodo. Questo è esattamente ciò che accade nel mio esempio: sto utilizzando into()
per convertire String
o &str
a Cow<str>
. Senza questa conversione esplicita si avrà un errore sui tipi non corrispondenti.
Grazie, funziona! Ma c'è molto che non capisco qui, come puoi restituire un 'A' di durata' statica' quando lo stai creando al volo? Non sono sicuro di quale sia la "variante di proprietà di" Mucca "e perché ciò rende possibile l'uso di" statico ". Non modifichiamo mai 'some_attr' in qualsiasi momento - di solito non è il punto di" copia su scrittura ", per consentire le scritture? 'Orig.to_owned' rimuove la proprietà da chiunque chiami questa funzione? Sembra che sarebbe scomodo. Per quanto ne so, 'String' implements' Into> 'ma non' str', quindi come possiamo chiamare 'into()' nel secondo esempio? –
wrongusername
Whoa, ci sono un sacco di domande! Ho aggiornato la mia risposta, si spera che sarebbe utile :) –