我正在编写一个解析器生成器作为学习 Rust 的项目,但我遇到了一些我无法用生命周期和闭包来解决的问题。这是我的简化案例(对不起,它很复杂,但我需要在真实版本中使用自定义迭代器,它似乎对编译器的行为产生了影响):
婴儿围栏链接:http://is.gd/rRm2aa
struct MyIter<'stat, T:Iterator<&'stat str>>{
source: T
}
impl<'stat, T:Iterator<&'stat str>> Iterator<&'stat str> for MyIter<'stat, T>{
fn next(&mut self) -> Option<&'stat str>{
self.source.next()
}
}
struct Scanner<'stat,T:Iterator<&'stat str>>{
input: T
}
impl<'main> Scanner<'main, MyIter<'main,::std::str::Graphemes<'main>>>{
fn scan_literal(&'main mut self) -> Option<String>{
let mut token = String::from_str("");
fn get_chunk<'scan_literal,'main>(result:&'scan_literal mut String,
input: &'main mut MyIter<'main,::std::str::Graphemes<'main>>)
-> Option<&'scan_literal mut String>{
Some(input.take_while(|&chr| chr != "\"")
.fold(result, |&mut acc, chr|{
acc.push_str(chr);
&mut acc
}))
}
get_chunk(&mut token,&mut self.input);
println!("token is {}", token);
Some(token)
}
}
fn main(){
let mut scanner = Scanner{input:MyIter{source:"\"foo\"".graphemes(true)}};
scanner.scan_literal();
}
我知道这里有两个问题。首先,我必须在 get_chunk 函数中隐藏“主生命周期”(我尝试使用
impl
中的那个,但编译器提示 'main
在 get_chunk
中未定义)。我认为它仍然会奏效,因为稍后对 get_chunk 的调用会将 'main
中的 impl
与 'main
中的 get_chunk
匹配,但我不确定这是否正确。第二个问题是闭包中的
&mut acc
需要有一个 'scan_literal
的生命周期才能像我想要的那样工作(在这个例子中遇到第一个 "
之前积累字符)。但是,我无法向 &mut acc
添加显式生命周期,并且编译器说它的生命周期仅限于闭包本身,因此我无法返回要在下一次折叠迭代中使用的引用。我已经获得了以各种其他方式编译和运行的函数,但我不明白这里的问题是什么。我的主要问题是:有没有办法明确指定闭包参数的生命周期?如果没有,是否有更好的方法使用 fold 来累积字符串而不进行多次复制?
最佳答案
首先,关于生命。定义在其他函数内部的函数是静态的,它们与外部代码没有任何联系。因此,它们的生命周期参数是完全独立的。您不想使用 'main
作为 get_chunk()
的生命周期参数,因为它会影响外部 'main
生命周期并且只会造成困惑。
接下来,关于闭包。这个表达:
|&mut acc, chr| ...
很可能不是您真正认为的那样。闭包/函数参数允许其中存在无可辩驳的模式,而
&
在模式中具有特殊意义。即,它取消引用它与之匹配的值,并将其标识符分配给这个取消引用的值:let x: int = 10i;
let p: &int = &x;
match p {
&y => println!("{}", y) // prints 10
}
您可以将模式中的
&
视为表达式中 &
的对立面:在表达式中它意味着“获取引用”,在模式中它意味着“删除引用”。然而
mut
不属于模式中的 &
;它属于标识符并意味着具有此标识符的变量是可变的,即你应该写不|&mut acc, chr| ...
但
|& mut acc, chr| ...
您可能对 this RFC 感兴趣,它正是关于语言语法中的这个怪癖。
看起来你想做一件很奇怪的事情,我不确定我理解你的意思。您很可能混淆了不同的字符串类型。首先,您应该阅读 the official guide ,它解释了所有权和借用以及何时使用它们(您可能还想阅读 unfinished ownership guide ;它很快就会进入主文档树),然后您应该阅读 strings guide 。
无论如何,您的问题可以通过更简单和通用的方式解决:
#[deriving(Clone)]
struct MyIter<'s, T: Iterator<&'s str>> {
source: T
}
impl<'s, T: Iterator<&'s str>> Iterator<&'s str> for MyIter<'s, T>{
fn next(&mut self) -> Option<&'s str>{ // '
self.source.next()
}
}
#[deriving(Clone)]
struct Scanner<'s, T: Iterator<&'s str>> {
input: T
}
impl<'m, T: Iterator<&'m str>> Scanner<'m, T> { // '
fn scan_literal(&mut self) -> Option<String>{
fn get_chunk<'a, T: Iterator<&'a str>>(input: T) -> Option<String> {
Some(
input.take_while(|&chr| chr != "\"")
.fold(String::new(), |mut acc, chr| {
acc.push_str(chr);
acc
})
)
}
let token = get_chunk(self.input.by_ref());
println!("token is {}", token);
token
}
}
fn main(){
let mut scanner = Scanner{
input: MyIter {
source: "\"foo\"".graphemes(true)
}
};
scanner.scan_literal();
}
您不需要将外部引用传递到闭包中;
String
操作中可以直接生成一个fold()
。我还对您的代码进行了泛化,使其更加地道。请注意,现在
impl
的 Scanner
也适用于返回 &str
的任意迭代器。很可能您想编写它而不是专门使用 Scanner
来仅使用其中包含 MyIter
的 Graphemes
。 by_ref()
操作将 &mut I
(其中 I
是 Iterator<T>
)转换为 J
,其中 J
是 Iterator<T>
。即使您只有对原始迭代器的可变引用,它也允许进一步链接迭代器。顺便说一句,你的代码也不完整;它只会返回
Some("")
因为 take_while()
将在第一个引用处停止并且不会进一步扫描。您应该重写它以考虑初始报价。关于debugging - Rust:如何在闭包参数中指定生命周期?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27213191/