string iterator rust ownership

¿Existe una versión propia de String:: chars?



iterator rust (5)

Como se copia de ¿Cómo puedo almacenar un iterador Chars en la misma estructura que la Cadena en la que está iterando? :

use std::mem; use std::str::Chars; /// I believe this struct to be safe because the String is /// heap-allocated (stable address) and will never be modified /// (stable address). `chars` will not outlive the struct, so /// lying about the lifetime should be fine. /// /// TODO: What about during destruction? /// `Chars` shouldn''t have a destructor... struct OwningChars { _s: String, chars: Chars<''static>, } impl OwningChars { fn new(s: String) -> Self { let chars = unsafe { mem::transmute(s.chars()) }; OwningChars { _s: s, chars } } } impl Iterator for OwningChars { type Item = char; fn next(&mut self) -> Option<Self::Item> { self.chars.next() } }

El siguiente código no se compila:

use std::str::Chars; struct Chunks { remaining: Chars, } impl Chunks { fn new(s: String) -> Self { Chunks { remaining: s.chars(), } } }

El error es:

error[E0106]: missing lifetime specifier --> src/main.rs:4:16 | 4 | remaining: Chars, | ^^^^^ expected lifetime parameter

Chars no posee los caracteres sobre los que itera y no puede sobrevivir al &str o String cual fue creado.

¿Existe una versión propia de Chars que no necesita un parámetro de por vida o tengo que mantener un Vec<char> y un índice yo mismo?


Puede implementar su propio iterador o envolver caracteres como este (con solo un pequeño bloque unsafe ):

// deriving Clone would be buggy. With Rc<>/Arc<> instead of Box<> it would work though. struct OwnedChars { // struct fields are dropped in order they are declared, // see https://.com/a/41056727/1478356 // with `Chars` it probably doesn''t matter, but for good style `inner` // should be dropped before `storage`. // ''static lifetime must not "escape" lifetime of the struct inner: ::std::str::Chars<''static>, // we need to box anyway to be sure the inner reference doesn''t move when // moving the storage, so we can erase the type as well. // struct OwnedChar<S: AsRef<str>> { ..., storage: Box<S> } should work too storage: Box<AsRef<str>>, } impl OwnedChars { pub fn new<S: AsRef<str>+''static>(s: S) -> Self { let storage = Box::new(s) as Box<AsRef<str>>; let raw_ptr : *const str = storage.as_ref().as_ref(); let ptr : &''static str = unsafe { &*raw_ptr }; OwnedChars{ storage: storage, inner: ptr.chars(), } } pub fn as_str(&self) -> &str { self.inner.as_str() } } impl Iterator for OwnedChars { // just `char` of course type Item = <::std::str::Chars<''static> as Iterator>::Item; fn next(&mut self) -> Option<Self::Item> { self.inner.next() } } impl DoubleEndedIterator for OwnedChars { fn next_back(&mut self) -> Option<Self::Item> { self.inner.next_back() } } impl Clone for OwnedChars { fn clone(&self) -> Self { // need a new allocation anyway, so simply go for String, and just // clone the remaining string OwnedChars::new(String::from(self.inner.as_str())) } } impl ::std::fmt::Debug for OwnedChars { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { let storage : &str = self.storage.as_ref().as_ref(); f.debug_struct("OwnedChars") .field("storage", &storage) .field("inner", &self.inner) .finish() } } // easy access trait StringExt { fn owned_chars(self) -> OwnedChars; } impl<S: AsRef<str>+''static> StringExt for S { fn owned_chars(self) -> OwnedChars { OwnedChars::new(self) } }

Ver playground


Puede usar la caja de alquiler para crear una estructura autorreferencial que contenga el iterador String y Chars :

#[macro_use] extern crate rental; rental! { mod into_chars { pub use std::str::Chars; #[rental] pub struct IntoChars { string: String, chars: Chars<''string>, } } } use into_chars::IntoChars; // All these implementations are based on what `Chars` implements itself impl Iterator for IntoChars { type Item = char; #[inline] fn next(&mut self) -> Option<Self::Item> { self.rent_mut(|chars| chars.next()) } #[inline] fn count(mut self) -> usize { self.rent_mut(|chars| chars.count()) } #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.rent(|chars| chars.size_hint()) } #[inline] fn last(mut self) -> Option<Self::Item> { self.rent_mut(|chars| chars.last()) } } impl DoubleEndedIterator for IntoChars { #[inline] fn next_back(&mut self) -> Option<Self::Item> { self.rent_mut(|chars| chars.next_back()) } } impl std::iter::FusedIterator for IntoChars {} // And an extension trait for convenience trait IntoCharsExt { fn into_chars(self) -> IntoChars; } impl IntoCharsExt for String { fn into_chars(self) -> IntoChars { IntoChars::new(self, |s| s.chars()) } }


También está la caja de caracteres propios , que

proporciona un rasgo de extensión para String con dos métodos, into_chars e into_char_indices. Estos métodos son paralelos a String :: chars y String :: char_indices, pero los iteradores que crean consumen la cadena en lugar de tomarla prestada.


std::vec::IntoIter es una versión propia de cada iterador, en cierto sentido.

use std::vec::IntoIter; struct Chunks { remaining: IntoIter<char>, } impl Chunks { fn new(s: String) -> Self { Chunks { remaining: s.chars().collect::<Vec<_>>().into_iter(), } } }

Enlace de juegos

Lo malo es la asignación adicional y una sobrecarga de espacio, pero no conozco el iterador para su caso específico.