简体   繁体   English

Rust:如何在闭包参数中指定生存期?

[英]Rust: How to specify lifetimes in closure arguments?

I'm writing a parser generator as a project to learn rust, and I'm running into something I can't figure out with lifetimes and closures. 我正在编写一个解析器生成器作为学习锈的项目,并且遇到了一些我无法使用生存期和结束时间解决的问题。 Here's my simplified case (sorry it's as complex as it is, but I need to have the custom iterator in the real version and it seems to make a difference in the compiler's behavior): 这是我的简化情况(很抱歉,它是如此复杂,但是我需要在实际版本中使用自定义迭代器,这似乎对编译器的行为有所影响):

Playpen link: http://is.gd/rRm2aa 游戏围栏链接: 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();
}

There are two problems I know of here. 我在这里知道两个问题。 First, I have to shadow the 'main lifetime in the get_chunk function (I tried using the one in the impl , but the compiler complains that 'main is undefined inside get_chunk ). 首先,我必须在get_chunk函数中隐藏主生命周期(我在impl尝试过使用它的主生命周期,但是编译器抱怨'main get_chunk内部未定义get_chunk )。 I think it will still work out because the call to get_chunk later will match the 'main from the impl with the 'main from get_chunk , but I'm not sure that's right. 我认为它仍然可以工作,因为调用后get_chunk将匹配'mainimpl'mainget_chunk ,但我不知道这是正确的。

The second problem is that the &mut acc inside the closure needs to have a lifetime of 'scan_literal in order to work like I want it to (accumulating characters until the first " is encountered for this example). I can't add an explicit lifetime to &mut acc though, and the compiler says its lifetime is limited to the closure itself, and thus I can't return the reference to use in the next iteration of fold. I've gotten the function to compile and run in various other ways, but I don't understand what the problem is here. 第二个问题是,闭包内的&mut acc的生存期必须为'scan_literal ,才能像我希望的那样工作(在此示例中,直到第一个"为止,一直累积字符)。我无法添加显式生存期但是,对于&mut acc ,编译器表示其生存期仅限于闭包本身,因此我无法返回引用以在fold的下一次迭代中使用。我已经获得了以各种其他方式进行编译和运行的函数,但我不明白这里是什么问题。

My main question is: Is there any way to explicitly specify the lifetime of an argument to a closure? 我的主要问题是:有没有办法明确指定闭包参数的生存期? If not, is there a better way to accumulate the string using fold without doing multiple copies? 如果不是,是否有更好的方法使用fold来累积字符串而不进行多次复制?

First, about lifetimes. 首先,关于一生。 Functions defined inside other functions are static, they are not connected with their outside code in any way. 在其他函数内部定义的函数是静态的,它们不以任何方式与其外部代码连接。 Consequently, their lifetime parameters are completely independent. 因此,它们的寿命参数是完全独立的。 You don't want to use 'main as a lifetime parameter for get_chunk() because it will shadow the outer 'main lifetime and give nothing but confusion. 您不希望将'main用作get_chunk()的生命周期参数,因为它会get_chunk()外部的'main生命周期,并且只会造成混乱。

Next, about closures. 接下来,关于闭包。 This expression: 该表达式:

|&mut acc, chr| ...

very likely does not what you really think it does. 很有可能不是您真正认为的那样。 Closure/function arguments allow irrefutable patterns in them, and & have special meaning in patterns. 闭包/函数参数允许使用不可辩驳的模式,并且&在模式中具有特殊含义。 Namely, it dereferences the value it is matched against, and assigns its identifier to this dereferenced value: 即,它取消引用与之匹配的值,并将其标识符分配给该取消引用的值:

let x: int = 10i;
let p: &int = &x;
match p {
    &y => println!("{}", y)  // prints 10
}

You can think of & in a pattern as an opposite to & in an expression: in an expression it means "take a reference", in a pattern it means "remove the reference". 你能想到的&在一个图案作为相反&中的表达式:在表达它的意思是“需要参考”,在它的意思是“删除引用”的图案。

mut , however, does not belong to & in patterns; 但是mut不属于&在模式中; it belongs to the identifier and means that the variable with this identifier is mutable, ie you should write not 它属于标识符,表示具有该标识符的变量是可变的,即您不应写

|&mut acc, chr| ...

but

|& mut acc, chr| ...

You may be interested in this RFC which is exactly about this quirk in the language syntax. 您可能对该RFC感兴趣,而该RFC恰恰与此语言语法方面的问题有关。

It looks like that you want to do a very strange thing, I'm not sure I understand where you're getting at. 看来您想做一件非常奇怪的事情,我不确定我了解您的去向。 It is very likely that you are confusing different string kinds. 您很可能混淆不同的字符串类型。 First of all, you should read the official guide which explains ownership and borrowing and when to use them (you may also want to read the unfinished ownership guide ; it will soon get into the main documentation tree), and then you should read strings guide . 首先,您应该阅读解释所有权和借用以及何时使用它们的官方指南 (您可能还想阅读未完成的所有权指南 ;它将很快进入主文档树),然后您应该阅读字符串指南。

Anyway, your problem can be solved in much simpler and generic way: 无论如何,您的问题可以通过更简单,更通用的方式解决:

#[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();
}

You don't need to pass external references into the closure; 您不需要将外部引用传递到闭包中。 you can generate a String directly in fold() operation. 您可以直接在fold()操作中生成一个String I also generified your code and made it more idiomatic. 我还简化了您的代码,使其更加惯用。

Note that now impl for Scanner also works with arbitrary iterators returning &str . 请注意,现在impl for Scanner也可以与返回&str任意迭代器一起使用。 It is very likely that you want to write this instead of specializing Scanner to work only with MyIter with Graphemes inside it. 这很可能是你想写的不是这个专业Scanner只与工作MyIterGraphemes里面。 by_ref() operation turns &mut I where I is an Iterator<T> into J , where J is an Iterator<T> . by_ref()操作将IIterator<T> &mut I转换为J ,其中JIterator<T> It allows further chaining of iterators even if you only have a mutable reference to the original iterator. 即使您只有对原始迭代器的可变引用,它也允许迭代器的进一步链接。

By the way, your code is also incomplete; 顺便说一下,您的代码也不完整。 it will only return Some("") because the take_while() will stop at the first quote and won't scan further. 它只会返回Some("")因为take_while()会停在第一个引号上,并且不会进一步扫描。 You should rewrite it to take initial quote into account. 您应该重写它以考虑初始报价。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM