工厂方法:实例寿命不够长
Factory method: instance does not live long enough
我正在用 Rust 开发一个单词生成器。该应用程序由两个主要结构组成:Letter
和 Alphabet
.
Letter
由单个字符和有关其与其他字母关系的规则组成。
Alphabet
包含元音和辅音的向量以及引用这些向量中的字母的哈希图。这样做是为了在 O(1) 时间内检索字母的规则。
我创建了一个工厂方法来从 json 字符串(下面的代码)中读取字母表,但是我收到一个错误,指出字母表实例的寿命不够长。
src/word_generator/alphabet.rs:47:6: 47:14 error: alphabet
does not
live long enough src/word_generator/alphabet.rs:47
alphabet.add_letter(letter);
src/word_generator/alphabet.rs:26:40: 55:3 note: reference must be
valid for the anonymous lifetime #1 defined on the block at 26:39...
src/word_generator/alphabet.rs:26 pub fn from_json(json:
&str)->Alphabet{
note: ...but borrowed value is only valid for the block suffix
following statement 3 at 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 的新手,所以欢迎任何改进代码的建议。
这是一个可能是您开始的小示例:
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() {}
你接着编译错误1:
<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() {}
根本问题在于2。在一般情况下,无论何时移动结构,所有成员变量的内存位置都会改变,从而使所有引用失效。这是一件坏事,Rust 会阻止你。
您收到的错误消息指出没有可能的生命周期可以满足您的要求 — 引用仅在结构不移动时有效,但您希望 return 从方法,移动它。
"But wait!"你说,"I have a Vec
and the contents of the Vec
are on the heap and won't move!"。虽然技术上是真实的(最好的真实),但 Rust 不会在那么细粒度的级别上跟踪事物。
这里的一般解决方案是将您的结构分成两部分。将 JSON 中的所有内容解析为仅包含 Vec
的 Alphabet
结构。然后将该结构(可能通过引用,也可能通过值)传递给 AlphabetSoup
结构。该结构可以一次性创建 HashMap
并提供一个位置来缓存您的值。
1 较新的编译器实际上 删除了 这个建议,因为误报率太高了高,它带来的混乱多于帮助。
2 你实际上 可以 引用你自己的成员,但你可以永远不要移动对象,这使得它在大多数情况下不切实际。
我正在用 Rust 开发一个单词生成器。该应用程序由两个主要结构组成:Letter
和 Alphabet
.
Letter
由单个字符和有关其与其他字母关系的规则组成。
Alphabet
包含元音和辅音的向量以及引用这些向量中的字母的哈希图。这样做是为了在 O(1) 时间内检索字母的规则。
我创建了一个工厂方法来从 json 字符串(下面的代码)中读取字母表,但是我收到一个错误,指出字母表实例的寿命不够长。
src/word_generator/alphabet.rs:47:6: 47:14 error:
alphabet
does not live long enough src/word_generator/alphabet.rs:47 alphabet.add_letter(letter);src/word_generator/alphabet.rs:26:40: 55:3 note: reference must be valid for the anonymous lifetime #1 defined on the block at 26:39... src/word_generator/alphabet.rs:26 pub fn from_json(json: &str)->Alphabet{
note: ...but borrowed value is only valid for the block suffix following statement 3 at 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 的新手,所以欢迎任何改进代码的建议。
这是一个可能是您开始的小示例:
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() {}
你接着编译错误1:
<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() {}
根本问题在于
您收到的错误消息指出没有可能的生命周期可以满足您的要求 — 引用仅在结构不移动时有效,但您希望 return 从方法,移动它。
"But wait!"你说,"I have a Vec
and the contents of the Vec
are on the heap and won't move!"。虽然技术上是真实的(最好的真实),但 Rust 不会在那么细粒度的级别上跟踪事物。
这里的一般解决方案是将您的结构分成两部分。将 JSON 中的所有内容解析为仅包含 Vec
的 Alphabet
结构。然后将该结构(可能通过引用,也可能通过值)传递给 AlphabetSoup
结构。该结构可以一次性创建 HashMap
并提供一个位置来缓存您的值。
1 较新的编译器实际上 删除了 这个建议,因为误报率太高了高,它带来的混乱多于帮助。
2 你实际上 可以 引用你自己的成员,但你可以永远不要移动对象,这使得它在大多数情况下不切实际。