如何从字符串创建迭代器并将其存储在结构中?
How to create an iterator from string _and_ store it in a struct?
我正在尝试为我的结构创建一个构造函数,它将存储一个迭代器 String
从文件中读取。问题是,一旦函数 returns,String
被删除,编译器就会抱怨 new() returns a value referencing data owned by the current function
。有没有办法以某种方式将 String
与结构相关联,以便它不会在 return 之后被删除?
我想我理解这里的抱怨,但我不知道如何处理它,因为我希望构造函数同时处理文件读取和迭代器创建。
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
}
}
String::chars()
返回的迭代器仅在原始字符串 input
存在期间有效。 input
在 new
的末尾被删除,因此无法从函数返回迭代器。
为了解决这个问题,您还想将 input
字符串存储在结构中,但是您 运行 陷入了其他问题,因为一个结构成员无法引用同一结构的另一个成员。原因之一是该结构将变得不可移动,因为移动它会使引用无效。
最简单的解决方案可能是将 char
收集到 Vec<char>
中并将该向量存储在 CharStream
中。然后添加一个 usize
索引并编写您自己的 Iterator<Item = char>
实现。
另一种方法(内存效率更高)是存储 String
本身,并根据需要创建 Chars
迭代器,但这当然会导致不同的 API .
涉及 RefCell
或类似包装器的解决方案可能也是可能的。
我会将 CharStream
重命名为 FileContents
并让它拥有文件的 filename
和 contents
作为 String
s。然后,当您需要生成 TokenIter
以迭代 contents
中的 char
块时,您可以按需创建 Chars<'a>
并将其传递给 TokenIter
然后。完整示例:
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
}
}
我正在尝试为我的结构创建一个构造函数,它将存储一个迭代器 String
从文件中读取。问题是,一旦函数 returns,String
被删除,编译器就会抱怨 new() returns a value referencing data owned by the current function
。有没有办法以某种方式将 String
与结构相关联,以便它不会在 return 之后被删除?
我想我理解这里的抱怨,但我不知道如何处理它,因为我希望构造函数同时处理文件读取和迭代器创建。
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
}
}
String::chars()
返回的迭代器仅在原始字符串 input
存在期间有效。 input
在 new
的末尾被删除,因此无法从函数返回迭代器。
为了解决这个问题,您还想将 input
字符串存储在结构中,但是您 运行 陷入了其他问题,因为一个结构成员无法引用同一结构的另一个成员。原因之一是该结构将变得不可移动,因为移动它会使引用无效。
最简单的解决方案可能是将 char
收集到 Vec<char>
中并将该向量存储在 CharStream
中。然后添加一个 usize
索引并编写您自己的 Iterator<Item = char>
实现。
另一种方法(内存效率更高)是存储 String
本身,并根据需要创建 Chars
迭代器,但这当然会导致不同的 API .
涉及 RefCell
或类似包装器的解决方案可能也是可能的。
我会将 CharStream
重命名为 FileContents
并让它拥有文件的 filename
和 contents
作为 String
s。然后,当您需要生成 TokenIter
以迭代 contents
中的 char
块时,您可以按需创建 Chars<'a>
并将其传递给 TokenIter
然后。完整示例:
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
}
}