简体   繁体   English

迭代器项的生命周期的“冲突要求”作为参数传递给方法

[英]“Conflicting requirements” for lifetime of item of iterator passed as parameter to method

I'm trying to get this code to compile: 我正在尝试编译这段代码:

use std::collections::HashMap;

#[derive(PartialEq, Eq, Hash, Clone)]
struct Key<'a> {
    v: &'a str
}

fn make_key_iter(s: &str) -> Box<Iterator<Item = Key>> {
    Box::new(s.split('.').map(|e| Key { v: e }))
}

struct Node<'a> {
    children: HashMap<Key<'a>, Box<Node<'a>>>
}

impl<'a> Node<'a> {
    fn lookup<'b>(&self, mut iter: Box<Iterator<Item = Key<'b>>>) -> bool {
        match iter.next() {
            Some(key) => match self.children.get(&key) {
                             Some(node) => node.lookup(iter),
                             None => false
                         },
            None => true
        }
    }
}

fn main() {
    let s = "a.b.c.d".to_string();
    let iter = make_key_iter(s.as_slice());
    let node = Node { children: HashMap::new() };
    node.lookup(iter);
}

Playpen link 围栏链接

Compiling that gives the following error: 编译时会出现以下错误:

<anon>:18:20: 18:26 error: cannot infer an appropriate lifetime due to conflicting requirements
<anon>:18         match iter.next() {
                         ^~~~~~
<anon>:17:5: 25:6 help: consider using an explicit lifetime parameter as shown: fn lookup(&self, mut iter: Box<Iterator<Item = Key<'b>>>) -> bool

What's really confusing is that the signature the compiler suggests is invalid altogether because it uses an undefined lifetime. 令人困惑的是,编译器建议的签名完全无效,因为它使用了未定义的生命周期。

First, one suggestion: Since a boxed iterator is also an iterator, you can change your lookup function to 首先,一个建议:由于盒装迭代器也是迭代器,您可以将查找函数更改为

fn lookup<'b, I: Iterator<Item = Key<'b>>>(&self, mut iter: I) -> bool {
    match iter.next() {
        Some(key) => match self.children.get(&key) {
                         Some(node) => node.lookup(iter),
                         None => false
                     },
        None => true
    }
}

which is a bit more general. 这有点笼统。 But the problem still persists. 但问题仍然存在。 You're trying to pass a &Key<'b> in self.children.get(&key) to the HashMap which actually expects a &Q where Q implements BorrowFrom<Key<'a>> . 你试图将self.children.get(&key)中的&Key<'b> self.children.get(&key)给HashMap,HashMap实际上需要一个&Q ,其中Q实现了BorrowFrom<Key<'a>> The compiler's suggestion is now to replace 'b with 'a like this: 编译器的建议现在用'a this 'a代替'b 'a

fn lookup<I: Iterator<Item = Key<'a>>>(&self, mut iter: I) -> bool { //'
    match iter.next() {
        Some(key) => match self.children.get(&key) {
                         Some(node) => node.lookup(iter),
                         None => false
                     },
        None => true
    }
}

which surely will make the compiler happy. 这肯定会让编译器开心。 But this is not really what you want! 但这不是你想要的! It would unnecessarily restrict the set of string slices you could use as parameters for your lookup. 它会不必要地限制您可以用作查找参数的字符串切片集。 This way, you can only use string slices which refer to memory that is at least as long-lived as the scope that 'a is referring to. 这样,您只能使用引用内存的字符串切片,该内存至少与'a指的范围一样长。 But for a lookup this restriction is not actually needed. 但是对于查找,实际上并不需要这种限制。

The solution is to completely get rid of any lifetime parameters in the type parameter Q of the HashMap's get function. 解决方案是在HashMap的get函数的类型参数Q中完全去掉任何生命周期参数。 Instead of using Q=Key<'something> , we can actually use Q=str . 我们实际上可以使用Q=str而不是使用Q=Key<'something> We just need to add the following BorrowFrom implementation 我们只需要添加以下BorrowFrom实现

impl<'a> BorrowFrom<Key<'a>> for str {
    fn borrow_from<'s>(owned: &'s Key<'a>) -> &'s str {
        owned.v
    }
}

and make the Key type public (since it's used as parameter in a public trait). 并将Key类型设为public(因为它在公共特征中用作参数)。 The lookup function that worked for me looks like this: 对我有用的查找功能如下所示:

fn lookup_iter<'b, I: Iterator<Item = Key<'b>>>(&self, mut i: I) -> bool {
    if let Some(key) = i.next() {
        match self.children.get(key.v) {
            Some(node_box_ref) => node_box_ref.lookup_iter(i),
            None => false
        }
    } else {
        true
    }
}

And if we piece everything together, we get 如果我们把所有东西拼凑起来,我们就会得到

#![feature(core)]
#![feature(hash)]
#![feature(std_misc)]
#![feature(collections)]

use std::collections::HashMap;
use std::collections::hash_map::Entry::{ Occupied, Vacant };
use std::borrow::BorrowFrom;

#[derive(PartialEq, Eq, Hash, Clone)]
pub struct Key<'a> {
    v: &'a str
}

impl<'a> BorrowFrom<Key<'a>> for str {
    fn borrow_from<'s>(owned: &'s Key<'a>) -> &'s str {
        owned.v
    }
}

fn str_to_key(s: &str) -> Key { 
    Key { v: s }
}

struct Node<'a> {
    children: HashMap<Key<'a>, Box<Node<'a>>>
}

impl<'a> Node<'a> {
    fn add_str(&mut self, s: &'a str) {
        self.add_iter(s.split('.').map(str_to_key))
    }

    fn add_iter<I>(&mut self, mut i: I) where I: Iterator<Item = Key<'a>> { //'
        if let Some(key) = i.next() {
            let noderef =
                match self.children.entry(key) {
                    Vacant(e) => {
                        let n = Node { children: HashMap::new() };
                        e.insert(Box::new(n))
                    }
                    Occupied(e) => {
                        e.into_mut()
                    }
                };
            noderef.add_iter(i);
        }
    }

    fn lookup_str(&self, s: &str) -> bool {
        self.lookup_iter(s.split('.').map(str_to_key))
    }

    fn lookup_iter<'b, I>(&self, mut i: I) -> bool where I: Iterator<Item = Key<'b>> {
        if let Some(key) = i.next() {
            match self.children.get(key.v) {
                Some(node_box_ref) => node_box_ref.lookup_iter(i),
                None => false
            }
        } else {
            true
        }
    }
}

fn main() {
    let mut node: Node<'static> = Node { children: HashMap::new() }; //'
    node.add_str("one.two.three");
    { // <-- "inner scope"
        let s = String::from_str("one.two.three");
        println!("lookup: {:?}", node.lookup_str(&*s));
    }
    println!("The End");
}

As you can see, I deliberately made node a Node<'static> , so the node's lifetime parameter 'a actually refers to the lifetime of the whole program. 如您所见,我故意使node成为Node<'static> ,因此节点的生命周期参数'a实际上指的是整个程序的生命周期。 It's OK in this example because the only string slice it will store is a string literal. 在这个例子中没关系,因为它将存储的唯一字符串切片是字符串文字。 Note that for the lookup I created a short-lived String object. 请注意,对于查找,我创建了一个短暂的String对象。 So, the lifetime parameter 'b in node.lookup_str will refer to the "inner scope" which is obviously shorter than 'a='static . 因此, node.lookup_str的生命周期参数'b将引用“内部范围”,它明显比'a='static短。 And it all works out! 一切顺利! :) :)

Oh, I also got rid of the iterator boxing. 哦,我也摆脱了迭代拳击。

I agree that the diagnostics are less-than-ideal. 我同意诊断不太理想。 I would recommend that a bug is filed; 我会建议提交一个bug; perhaps that suggester doesn't know about lifetimes in associated types yet. 也许那个建议者还不知道相关类型的生命周期。

To fix your problem, I'd suggest using the same lifetime that you are already holding: 为了解决您的问题,我建议使用您已经持有的相同生命周期:

impl<'a> Node<'a> {
    fn lookup(&self, mut iter: Box<Iterator<Item = Key<'a>>>) -> bool { //'
        match iter.next() {
            Some(key) => match self.children.get(&key) {
                             Some(node) => node.lookup(iter),
                             None => false
                         },
            None => true
        }
    }
}

I actually am unclear what your original code was trying to do. 我实际上不清楚你的原始代码试图做什么。 You define a new lifetime parameter 'b . 您定义了一个新的生命周期参数'b That lifetime will be determined by the caller for each call made. 每个呼叫都会由呼叫者确定该生命周期。 That's bad news, because that lifetime might last longer than the Node itself, leading to references to memory that are no longer valid. 这是个坏消息,因为生命周期可能比Node本身持续更长时间,从而导致对不再有效的内存的引用。 Yay! 好极了! Rust saved us! Rust拯救了我们!

Another solution would be to have an explicit lifetime 'b , but inform Rust that it's shorter or equal to than 'a (said "a outlives b") longer than or equal to 'a (said "b outlives a"): 另一个解决方案是有一个明确的生命周期'b ,但告知Rust它'a (表示“a outlives b”)更长或更等于'a (表示“b outlives a”):

fn lookup<'b : 'a>(&self, mut iter: Box<Iterator<Item = Key<'b>>>) -> bool

Further exposition 进一步阐述

Here's a smaller example that shows the same problem: 这是一个显示相同问题的小例子:

use std::collections::HashSet;

fn example<'a, 'b>(set: HashSet<&'a u8>, key: &'b u8) -> bool {
    set.contains(&key)
}

fn main() {}

Similarly, if you setup a relationship between the lifetimes ( <'a, 'b : 'a> ) or make both parameters the same lifetime, then this code will compile. 同样,如果您在生命周期之间建立关系( <'a, 'b : 'a> )或使两个参数的生命周期相同,那么此代码将进行编译。

Both HashSet::contains and HashMap::get lookup the key by taking a reference to the key type (or something that can lend out a reference to the key type). HashSet::containsHashMap::get通过引用键类型(或者可以提供对键类型的引用的东西)来查找键。 However, the key that you are looking up must be the same type (or subtype) that you have stored. 但是,您正在查找的密钥必须与您存储的密钥类型(或子类型)相同。 In this case, the type also includes the lifetime . 在这种情况下,类型还包括生命周期 That's why using the same lifetime (or one that outlives the key) allows it to compile. 这就是为什么使用相同的生命周期(或一个超过密钥的生命周期)允许它编译。

暂无
暂无

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

相关问题 function 调用中的生命周期参数有冲突的要求 - Lifetime parameter in function call has conflicting requirements 实现迭代器的代理方法,生命周期冲突 - Proxy method for implementing Iterator, conflicting lifetime 尝试实现迭代器:由于需求冲突而无法推断出适当的生存期 - Trying to implement an iterator: cannot infer an appropriate lifetime due to conflicting requirements 如何解决传递给闭包的 &amp;PathBuf 的冲突生命周期要求? - How can I solve conflicting lifetime requirements for a &PathBuf passed to a closure? 作为函数参数的闭包“由于需求冲突而无法推断出适当的寿命” - Closure as function parameter “cannot infer an appropriate lifetime due to conflicting requirements” 将结构转换为具有生存期的特征得到“由于需求冲突,无法为生存期参数&#39;a&#39;推断适当的生存期” - casting struct to trait with lifetime got “cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements” 如何解决“冲突的生命周期要求” - How to fix 'Conflicting lifetime requirements' 由于对由具有 Serde Deserialize 的枚举组成的结构的要求相互冲突,因此无法为生命周期参数“de”推断合适的生命周期 - cannot infer an appropriate lifetime for lifetime parameter `'de` due to conflicting requirements for struct made of enums with Serde Deserialize "由于要求冲突,无法推断函数调用中生命周期参数 &#39;_ 的适当生命周期" - cannot infer an appropriate lifetime for lifetime parameter '_ in function call due to conflicting requirements 递归关闭作为函数参数“由于需求冲突而无法推断出适当的寿命” - Recursive closure as function parameter “cannot infer an appropriate lifetime due to conflicting requirements”
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM