首页 文章

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

提问于
浏览
1

我正在编写一个解析器生成器作为学习生锈的项目,而且我遇到了一些我无法用生命和封闭来解决的问题 . 这是我的简化案例(抱歉它虽然复杂,但我需要在真实版本中使用自定义迭代器,它似乎会对编译器的行为产生影响):

围栏链接: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的调用将匹配来自 impl'main 与来自 get_chunk'main ,但我'm not sure that'是正确的 .

第二个问题是闭包内的 &mut acc 需要有 'scan_literal 的生命周期才能像我想要的那样工作(在本例中遇到第一个 " 之前累积字符) . 我不能为 &mut acc 添加显式生命周期,并且编译器说它的生命周期仅限于闭包本身,因此我可以't return the reference to use in the next iteration of fold. I'得到函数来编译和以其他各种方式运行,但我不明白是什么问题出在这里 .

我的主要问题是:有没有办法明确指定闭包的参数的生命周期?如果没有,是否有更好的方法使用折叠累积字符串而不进行多个副本?

1 回答

  • 3

    首先,关于生命 . 在其他函数内定义的函数是静态的,它们不以任何方式与它们的外部代码连接 . 因此,它们的寿命参数是完全独立的 . 您不希望将 'main 用作 get_chunk() 的生命周期参数,因为它会影响外部 'main 生命周期并且只会产生混淆 .

    接下来,关于闭包 . 这个表达式:

    |&mut acc, chr| ...
    

    很可能不是你真正认为的那样 . 闭包/函数参数允许其中包含无可辩驳的模式, & 在模式中具有特殊含义 . 也就是说,它取消引用它匹配的值,并将其标识符分配给此解除引用的值:

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

    您可以将模式中的 & 视为与表达式中的 & 相反:在表达式中它表示"take a reference",在模式中它表示"remove the reference" .

    但是, 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();
    }
    

    您不需要将外部引用传递给闭包;您可以直接在 fold() 操作中生成 String . 我还对你的代码进行了整合,使其更具惯用性 .

    注意,现在 impl for Scanner 也适用于返回 &str 的任意迭代器 . 很可能你想写这个而不是专门化 Scanner 只能用 MyIterGraphemes 一起工作 . by_ref() 操作变为 &mut I ,其中 IIterator<T>J ,其中 JIterator<T> . 它允许进一步链接迭代器,即使您只有对原始迭代器的可变引用 .

    顺便说一下,你的代码也是不完整的;它只会返回 Some("") ,因为 take_while() 将在第一个引号处停止,并且不会进一步扫描 . 您应该重写它以考虑初始报价 .

相关问题