¿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(),
}
}
}
Lo malo es la asignación adicional y una sobrecarga de espacio, pero no conozco el iterador para su caso específico.