首页 文章

工厂方法:实例寿命不够长

提问于
浏览
1

我在Rust开发一个单词生成器 . 该应用程序包含两个主要结构: LetterAlphabet .

Letter 由单个字符和与其他字母关系的规则组成 .

Alphabet 包含元音和辅音的向量,以及带有对这些向量中字母的引用的散列映射 . 这样做是为了可以在O(1)时间内检索字母规则 .

我已经创建了一个工厂方法来从json字符串读取字母表(下面的代码),但我收到一个错误,说明字母表实例的活动时间不够长 .

src / word_generator / alphabet.rs:47:6:47:14错误:字母表的寿命不够src / word_generator / alphabet.rs:47 alphabet.add_letter(letter); src / word_generator / alphabet.rs:26:40:55:3注意:引用必须对26:39块上定义的匿名生命#1有效... src / word_generator / alphabet.rs:26 pub fn from_json (json:&str) - > Alphabet {注意:...但借用的值仅对于语句3后面的块后缀有效,在40:37 src / word_generator / alphabet.rs:40 let mut alphabet = Alphabet :: new() ; SRC / word_generator / alphabet.rs:41

我理解错误(我希望),但我不明白为什么会发生 . 为什么函数 new() 返回的 Alphabet 实例被变量 alphabet 借用?这不是一个移动操作吗?

pub struct Alphabet<'a>{
    pub vowels: Vec<Letter>,
    pub consonants: Vec<Letter>,
    letters: HashMap<char,&'a Letter>
}

impl<'a> Alphabet<'a>{

    pub fn new()->Alphabet<'a>{

        return Alphabet{
            vowels: Vec::new(),
            consonants: Vec::new(),
            letters: HashMap::new()
        }

    }

    pub fn from_json(json: &str)->Alphabet{

        let data :Json = match Json::from_str(json){
            Ok(_data)=>_data,
            Err(_err)=>panic!("Invalid JSON provided")
        };

        let letters = match data.as_array(){
            Some(_letters)=>_letters,
            None=>panic!("Expected JSON\'s root to be an array but found a different structure.")
        };

        let mut it = letters.iter();

        let mut alphabet = Alphabet::new();

        loop {
            match it.next(){
                Some(x) =>{

                    let letter : Letter= json::decode(&(x.to_string())).unwrap();
                    alphabet.add_letter(letter);

                },
                None => break,
            }
        }

        return alphabet
    }

    fn add_letter(&'a mut self,ref l: Letter){

        match l.letter_type {
            LetterType::Vowel =>{
                self.vowels.push(l.clone());
                self.letters.insert(l.value, &self.vowels.last().unwrap());
            },
            LetterType::Consonant =>{ 
                self.consonants.push(l.clone());
                self.letters.insert(l.value, &self.consonants.last().unwrap());
            }
        }

    }
}

P.S . :我对Rust很新,因此欢迎任何改进代码的建议 .

1 回答

  • 3

    这是一个较小的例子,可能是你开始的地方:

    use std::collections::HashMap;
    
    struct Letter;
    
    struct Alphabet<'a>{
        vowels: Vec<Letter>,
        letters: HashMap<u8, &'a Letter>
    }
    
    impl<'a> Alphabet<'a> {
        fn add(&mut self, l: Letter) {
            self.vowels.push(l);
            self.letters.insert(42, &self.vowels.last().unwrap());
        }
    }
    
    fn main() {}
    

    然后,您遵循编译器error1:

    <anon>:12:46: 12:52 error: cannot infer an appropriate lifetime for lifetime parameter 'a in function call due to conflicting requirements
    <anon>:12         self.letters.insert(42, &self.vowels.last().unwrap());
                                                           ^~~~~~
    <anon>:10:5: 13:6 help: consider using an explicit lifetime parameter as shown: fn add(&'a mut self, l: Letter)
    

    继续,直到你得到这样的东西:

    use std::collections::HashMap;
    
    struct Letter;
    
    struct Alphabet<'a>{
        vowels: Vec<Letter>,
        letters: HashMap<u8, &'a Letter>
    }
    
    impl<'a> Alphabet<'a> {
        fn new() -> Alphabet<'a> {
            Alphabet { vowels: Vec::new(), letters: HashMap::new() }
        }
    
        fn add(&'a mut self, l: Letter) {
            self.vowels.push(l);
            self.letters.insert(42, &self.vowels.last().unwrap());
        }
    
        fn parse() -> Alphabet<'a> {
            let mut a = Alphabet::new();
            a.add(Letter);
            a
        }
    }
    
    fn main() {}
    

    根本问题是you cannot have references to your own members 2.在一般情况下,无论何时移动结构,所有成员变量的内存位置都会改变,使所有引用无效 . 这是件坏事,Rust会阻止你 .

    您获得的错误消息指出's no possible lifetime that can satisfy what you want — the reference is only valid while the struct doesn't移动,但您想从方法返回结构,移动它 .

    "But wait!"你说,“我有一个 VecVec 的内容在堆上,赢得't move!". While technically true (the best kind of true), Rust doesn'跟踪那个细粒度的东西 .

    这里的一般解决方案是将您的结构分成两部分 . 仅使用 Vec 将JSON中的所有内容解析为 Alphabet 结构 . 然后将该结构(可能通过引用,可能通过值)传递给 AlphabetSoup 结构 . 该结构可以同时创建 HashMap 并提供缓存值的位置 .

    1较新的编译器实际上删除了这个建议,因为误报率太高而且引入了比它帮助更多的混乱 .

    2实际上你可以引用自己的成员,但是你永远不能移动对象,这使得它在大多数情况下都不实用 .

相关问题