[英]How to create an iterator from string _and_ store it in a struct?
I'm trying to create a constructor for my struct, which would store an iterator over String
read from file.我正在尝试为我的结构创建一个构造函数,它将在从文件读取的
String
上存储一个迭代器。 The problem is that once the functions returns, String
is dropped and compiler complains new() returns a value referencing data owned by the current function
.问题是一旦函数返回,
String
被删除并且编译器抱怨new() returns a value referencing data owned by the current function
。 Is there a way to associate String
with a struct somehow so that it is not dropped after return?有没有办法以某种方式将
String
与结构相关联,以便在返回后不会删除它?
I think I understand a complaint here but I don't understand how to deal with it, because I want constructor to deal both with file reading and iterator creation.我想我在这里理解一个抱怨,但我不明白如何处理它,因为我希望构造函数同时处理文件读取和迭代器创建。
pub struct CharStream<'a> {
input: std::str::Chars<'a>,
filename: String,
}
impl<'a> CharStream<'a> {
pub fn new(filename: String) -> CharStream<'a> {
let mut file = File::open(&filename).unwrap();
let mut input = String::new();
file.read_to_string(&mut input);
CharStream {
input: input.chars(), // Create an iterator over `input`
filename: filename,
}
// `input` is dropped here
}
}
I would rename CharStream
into FileContents
and let it own both the filename
and contents
of the file as String
s.我会将
CharStream
重命名为FileContents
并让它拥有文件的filename
和contents
作为String
s。 Then when you need to produce a TokenIter
to iterate over chunks of char
s from the contents
you can then create the Chars<'a>
on-demand and pass it to TokenIter
then.然后,当您需要生成
TokenIter
以从contents
中迭代char
块时,您可以按需创建Chars<'a>
并将其传递给TokenIter
。 Complete example:完整示例:
use std::fs;
use std::str::Chars;
struct FileContents {
filename: String,
contents: String,
}
impl FileContents {
fn new(filename: String) -> Self {
let contents = fs::read_to_string(&filename).unwrap();
FileContents { filename, contents }
}
fn token_iter(&self) -> TokenIter<'_> {
TokenIter {
chars: self.contents.chars(),
}
}
}
struct TokenIter<'a> {
chars: Chars<'a>,
}
struct Token; // represents some chunk of chars
impl<'a> Iterator for TokenIter<'a> {
type Item = Token;
fn next(&mut self) -> Option<Self::Item> {
self.chars.next(); // call as many times as necessary to create token
Some(Token) // return created token here
}
}
fn example(filename: String) {
let file_contents = FileContents::new(filename);
let tokens = file_contents.token_iter();
for token in tokens {
// more processing here
}
}
The iterator returned by String::chars()
is only valid as long as the original string input
lives. String::chars()
返回的迭代器仅在原始字符串input
存在时才有效。 input
is dropped at the end of new
, so the iterator cannot be returned from the function. input
在new
的末尾被删除,因此无法从 function 返回迭代器。
To solve this, you'd want to store the input
string in the struct as well, but then you run into other problems because one struct member can't have a reference to another member of the same struct.为了解决这个问题,您还希望将
input
字符串存储在结构中,但随后您会遇到其他问题,因为一个结构成员不能引用同一结构的另一个成员。 One reason for this is that the struct would become immovable, since moving it would invalidate the reference.这样做的一个原因是结构将变得不可移动,因为移动它会使引用无效。
The simplest solution is probably to collect the char
s into a Vec<char>
and store that vector inside the CharStream
.最简单的解决方案可能是将
char
收集到Vec<char>
并将该向量存储在CharStream
中。 Then add an usize
index and write your own Iterator<Item = char>
implementation.然后添加一个
usize
索引并编写您自己的Iterator<Item = char>
实现。
Another approach (more memory-efficient) is to store the String
itself, and create the Chars
iterator on demand, but that would of course result in a different API.另一种方法(更节省内存)是存储
String
本身,并按需创建Chars
迭代器,但这当然会导致不同的 API。
Solutions involving RefCell
or similar wrappers are probably also possible.涉及
RefCell
或类似包装器的解决方案也可能是可能的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.