如何将(String,String)元组上的迭代器转换为(&str,&str)的迭代器?

blu*_*yll 3 lifetime rust

我无法从Iterator转换(String, String)为Iterator (&str, &str).我正在使用外部库,因此无法更改其签名,也不确定是否需要.基本上我有这个功能def:

use hyper;

fn build_url<'a, I>(host: &'a str, port: u16, path: &'a str, params: I) -> 
   hyper::Url where I: Iterator<Item=(String, String)> {

       let mut url = hyper::Url::parse(&format!("http://{h}:{p}/{pt}",
                                            h = self.etcd_host,
                                            p = self.etcd_port,
                                            pt = path));
       if let Err(e) = url {
           panic!("error parsing url: {}", e);
       }

       let mut url = url.unwrap();

       // fn set_query_from_pairs<'a, I>(&mut self, pairs: I)
       //  where I: Iterator<Item=(&'a str, &'a str)>
       url.set_query_from_pairs(
            params.map(|x: (String, String)| -> 
                       (&str, &str) { let (ref k, ref v) = x; (k, v) } ));

}
Run Code Online (Sandbox Code Playgroud)

但我得到了可怕的: error: 'x.0' does not live long enough

我认为reflet中的关键字应该是正确的,即保持所有权与迭代器,并只是借用.我得到了一个类似的问题,如果我摆脱ref让let更改let to this:

let (k, v) = x; (&k, &v)
Run Code Online (Sandbox Code Playgroud)

然后k,v不要活得足够长.有没有人有建议解决这个问题?

DK.*_*DK. 6

你不能拥有一个(安全地)产生对任何内部或拥有状态的引用的迭代器; 这个Iterator特性并不是为了让它成为可能.这些类型的构造通常被称为"流式迭代器",而且它们目前在语言/ stdlib中是一个漏洞.

考虑(String, String)值在流经您的map呼叫时会发生什么.每个元组都会从中返回I::next,这会导致所有权传递到您给出的闭包中map.因此,当您ref在闭包中使用时,您将引用闭包本地的变量.你现在构造一个新元组,返回它并...因为闭包拥有Strings(它们存储在k和中v),它们被销毁,从而使你试图返回的引用无效.

问题是没有办法避免取得(String, String)物品的所有权.

现在,说过,你可以在这里作弊.您需要做的就是保证(String, String)值在迭代器中的每个步骤之外继续存在.从而:

let params: Vec<_> = params.collect();
url.set_query_from_pairs(params.iter().map(|&(ref x, ref y)| (&x[..], &y[..])))
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为Vec::iter给了我们Iterator<Item=&(String, String)>,我们可以从中借用而不取得所有权(由其保留params).