从 Option<T<'a>> 获取 &'a T
Aquire &'a T from Option<T<'a>>
恐怕这可能是非常基础的,但我自己还没有弄明白。我有这张地图:
subscriptions_map: HashMap<SubscriptionKey, Subscription<'a>>
和这个向量:
subscriptions: Vec<&'a Subscription<'a>>,
我想在 HashMap
中插入一个值,并在向量中插入对同一项目的引用。我试过这样做:
let subs: &'a Subscription = &self.subscriptions_map.insert(id, item).unwrap();
self.subscriptions.push(subs);
但出现此错误:
error: borrowed value does not live long enough
let subs: &'a Subscription = &self.subscriptions_map.insert(id, item).unwrap();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: reference must be valid for the lifetime 'a as defined on the block at 40:70...
pub fn add_subscription(&'a mut self, mut item: Subscription<'a>) {
let id = item.get_id();
let _lock = self.lock.lock().unwrap();
let subs: &'a Subscription = &self.subscriptions_map.insert(id, item).unwrap();
...
note: ...but borrowed value is only valid for the block suffix following statement 2 at 45:87
let subs: &'a Subscription = &self.subscriptions_map.insert(id, item).unwrap();
self.subscriptions.push(subs);
}
error: aborting due to previous error
我想我的问题可以归结为:如果我有一个 Option<T<'a>>
,我怎样才能得到一个 &'a T
?
HashMap.insert()
returns 给定键的 old 值,而不是您刚刚传递的值。这不是你想要的!
将项目插入 HashMap
后,您必须调用 HashMap.get()
来检索指向该值的指针。由于 HashMap.insert()
同时拥有键和值的所有权,我们需要将 id
的克隆传递给 insert()
以便我们可以将原始 id
用于 get()
打电话。 (如果id
的类型是Copy
,你可以省略对clone()
的调用,让编译器复制这个值。)
use std::collections::HashMap;
#[derive(Eq, PartialEq, Hash, Clone)]
struct SubscriptionKey;
struct Subscription<'a>(&'a ());
struct Foo<'a> {
subscriptions_map: HashMap<SubscriptionKey, Subscription<'a>>,
subscriptions: Vec<&'a Subscription<'a>>,
}
impl<'a> Foo<'a> {
fn add(&'a mut self, id: SubscriptionKey, item: Subscription<'a>) {
self.subscriptions_map.insert(id.clone(), item);
let subs = self.subscriptions_map.get(&id).unwrap();
self.subscriptions.push(subs);
}
}
fn main() {
let subscription_data = &();
let mut f = Foo {
subscriptions_map: HashMap::new(),
subscriptions: Vec::new(),
};
f.add(SubscriptionKey, Subscription(subscription_data));
}
这工作正常,但如果我们尝试添加另一个订阅,它就会崩溃。如果我们这样做:
fn main() {
let subscription_data = &();
let subscription_data2 = &();
let mut f = Foo {
subscriptions_map: HashMap::new(),
subscriptions: Vec::new(),
};
f.add(SubscriptionKey, Subscription(subscription_data));
f.add(SubscriptionKey, Subscription(subscription_data2));
}
编译器给出以下信息:
<anon>:30:5: 30:6 error: cannot borrow `f` as mutable more than once at a time [E0499]
<anon>:30 f.add(SubscriptionKey, Subscription(subscription_data2));
^
<anon>:30:5: 30:6 help: see the detailed explanation for E0499
<anon>:29:5: 29:6 note: previous borrow of `f` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `f` until the borrow ends
<anon>:29 f.add(SubscriptionKey, Subscription(subscription_data));
^
<anon>:31:2: 31:2 note: previous borrow ends here
<anon>:20 fn main() {
...
<anon>:31 }
^
这是怎么回事?为什么可变借用在第一次调用 Foo::add
后仍然存在?
问题出在subscriptions
字段的定义上。它被定义为 Vec<&'a Subscription<'a>>
。满足 Subscription<'a>
中的 'a
很容易,因为我们在 add
中收到了具有正确生命周期的对象。满足 &'a ...
中的 'a
更难,因为 Subscription<'a>
值在我们将其插入 subscriptions_map
之前没有固定地址(在我的示例中, Subscription<'a>
从 main()
中的局部变量移动到 Foo::add()
中的参数到 self.subscriptions_map
内部)。
为了满足外'a
,Foo::add()
必须定义其self
参数为&'a mut self
。如果我们将它定义为 &mut self
,我们不能确定我们从 subscriptions_map
中得到的引用是否足够长(它们的生命周期可能比 'a
短)。
但是,通过在 Foo<'a>
中插入 &'a Subscription<'a>
,我们有效地 锁定 Foo
以进行进一步修改,因为我们现在将 self.subscriptions_map
的借用存储在 self.subscriptions
中。考虑一下如果我们在 subscriptions_map
中插入另一个项目会发生什么:我们如何确定 HashMap
不会在内存中移动它的项目?如果 HashMap
确实移动了我们的项目,self.subscriptions
中的指针将不会自动更新并且会悬空。
现在,假设我们有这个有问题的 remove()
方法:
impl<'a> Foo<'a> {
fn remove(&mut self, id: &SubscriptionKey) {
self.subscriptions_map.remove(id);
}
}
此方法编译正常。但是,如果我们试图在我们之前调用过 add()
的 Foo
上调用它,那么 self.subscriptions
将包含对曾经在 self.subscriptions_map
中的项目的悬空引用.
所以可变借用在调用 add()
后仍然存在的原因是,由于 Foo<'a>
中的 'a
等于 Foo<'a>
本身的生命周期,编译器认为该对象是从自身借用的。如你所知,我们不能同时激活一个可变借用和另一个借用(可变或不可变),所以 Rust 阻止我们在 f
上进行可变借用,而 f
本身保留了一个主动借。事实上,由于我们使用了一个通过可变引用获取 self
的方法,Rust 假设 Foo<'a>
存储了一个可变引用,尽管事实并非如此,因为 Rust 只查看签名来确定借用(这是为了确保将私有字段从 &'a T
更改为 &'a mut T
不会对您造成借用检查失败,如果您正在开发库,对您的用户也是如此)。由于对象的类型永远不会改变,因此 Foo<'a>
在其剩余生命周期内被锁定。
现在,你能做什么?显然,您不能在结构中使用 Vec<&'a Subscription<'a>>
。 HashMap
提供了一个 values()
iterator, but it enumerates the values in an unspecified order, so it won't help you if you want to enumerate the values in the order in which they were added. Instead of using borrowed pointers, you could use Rc
:
use std::collections::HashMap;
use std::rc::Rc;
#[derive(Eq, PartialEq, Hash)]
struct SubscriptionKey;
struct Subscription<'a>(&'a ());
struct Foo<'a> {
subscriptions_map: HashMap<SubscriptionKey, Rc<Subscription<'a>>>,
subscriptions: Vec<Rc<Subscription<'a>>>,
}
impl<'a> Foo<'a> {
fn add(&mut self, id: SubscriptionKey, item: Subscription<'a>) {
let item = Rc::new(item);
self.subscriptions_map.insert(id, item.clone());
self.subscriptions.push(item);
}
}
fn main() {
let subscription_data = &();
let mut f = Foo {
subscriptions_map: HashMap::new(),
subscriptions: Vec::new(),
};
f.add(SubscriptionKey, Subscription(subscription_data));
}
恐怕这可能是非常基础的,但我自己还没有弄明白。我有这张地图:
subscriptions_map: HashMap<SubscriptionKey, Subscription<'a>>
和这个向量:
subscriptions: Vec<&'a Subscription<'a>>,
我想在 HashMap
中插入一个值,并在向量中插入对同一项目的引用。我试过这样做:
let subs: &'a Subscription = &self.subscriptions_map.insert(id, item).unwrap();
self.subscriptions.push(subs);
但出现此错误:
error: borrowed value does not live long enough
let subs: &'a Subscription = &self.subscriptions_map.insert(id, item).unwrap();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: reference must be valid for the lifetime 'a as defined on the block at 40:70...
pub fn add_subscription(&'a mut self, mut item: Subscription<'a>) {
let id = item.get_id();
let _lock = self.lock.lock().unwrap();
let subs: &'a Subscription = &self.subscriptions_map.insert(id, item).unwrap();
...
note: ...but borrowed value is only valid for the block suffix following statement 2 at 45:87
let subs: &'a Subscription = &self.subscriptions_map.insert(id, item).unwrap();
self.subscriptions.push(subs);
}
error: aborting due to previous error
我想我的问题可以归结为:如果我有一个 Option<T<'a>>
,我怎样才能得到一个 &'a T
?
HashMap.insert()
returns 给定键的 old 值,而不是您刚刚传递的值。这不是你想要的!
将项目插入 HashMap
后,您必须调用 HashMap.get()
来检索指向该值的指针。由于 HashMap.insert()
同时拥有键和值的所有权,我们需要将 id
的克隆传递给 insert()
以便我们可以将原始 id
用于 get()
打电话。 (如果id
的类型是Copy
,你可以省略对clone()
的调用,让编译器复制这个值。)
use std::collections::HashMap;
#[derive(Eq, PartialEq, Hash, Clone)]
struct SubscriptionKey;
struct Subscription<'a>(&'a ());
struct Foo<'a> {
subscriptions_map: HashMap<SubscriptionKey, Subscription<'a>>,
subscriptions: Vec<&'a Subscription<'a>>,
}
impl<'a> Foo<'a> {
fn add(&'a mut self, id: SubscriptionKey, item: Subscription<'a>) {
self.subscriptions_map.insert(id.clone(), item);
let subs = self.subscriptions_map.get(&id).unwrap();
self.subscriptions.push(subs);
}
}
fn main() {
let subscription_data = &();
let mut f = Foo {
subscriptions_map: HashMap::new(),
subscriptions: Vec::new(),
};
f.add(SubscriptionKey, Subscription(subscription_data));
}
这工作正常,但如果我们尝试添加另一个订阅,它就会崩溃。如果我们这样做:
fn main() {
let subscription_data = &();
let subscription_data2 = &();
let mut f = Foo {
subscriptions_map: HashMap::new(),
subscriptions: Vec::new(),
};
f.add(SubscriptionKey, Subscription(subscription_data));
f.add(SubscriptionKey, Subscription(subscription_data2));
}
编译器给出以下信息:
<anon>:30:5: 30:6 error: cannot borrow `f` as mutable more than once at a time [E0499]
<anon>:30 f.add(SubscriptionKey, Subscription(subscription_data2));
^
<anon>:30:5: 30:6 help: see the detailed explanation for E0499
<anon>:29:5: 29:6 note: previous borrow of `f` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `f` until the borrow ends
<anon>:29 f.add(SubscriptionKey, Subscription(subscription_data));
^
<anon>:31:2: 31:2 note: previous borrow ends here
<anon>:20 fn main() {
...
<anon>:31 }
^
这是怎么回事?为什么可变借用在第一次调用 Foo::add
后仍然存在?
问题出在subscriptions
字段的定义上。它被定义为 Vec<&'a Subscription<'a>>
。满足 Subscription<'a>
中的 'a
很容易,因为我们在 add
中收到了具有正确生命周期的对象。满足 &'a ...
中的 'a
更难,因为 Subscription<'a>
值在我们将其插入 subscriptions_map
之前没有固定地址(在我的示例中, Subscription<'a>
从 main()
中的局部变量移动到 Foo::add()
中的参数到 self.subscriptions_map
内部)。
为了满足外'a
,Foo::add()
必须定义其self
参数为&'a mut self
。如果我们将它定义为 &mut self
,我们不能确定我们从 subscriptions_map
中得到的引用是否足够长(它们的生命周期可能比 'a
短)。
但是,通过在 Foo<'a>
中插入 &'a Subscription<'a>
,我们有效地 锁定 Foo
以进行进一步修改,因为我们现在将 self.subscriptions_map
的借用存储在 self.subscriptions
中。考虑一下如果我们在 subscriptions_map
中插入另一个项目会发生什么:我们如何确定 HashMap
不会在内存中移动它的项目?如果 HashMap
确实移动了我们的项目,self.subscriptions
中的指针将不会自动更新并且会悬空。
现在,假设我们有这个有问题的 remove()
方法:
impl<'a> Foo<'a> {
fn remove(&mut self, id: &SubscriptionKey) {
self.subscriptions_map.remove(id);
}
}
此方法编译正常。但是,如果我们试图在我们之前调用过 add()
的 Foo
上调用它,那么 self.subscriptions
将包含对曾经在 self.subscriptions_map
中的项目的悬空引用.
所以可变借用在调用 add()
后仍然存在的原因是,由于 Foo<'a>
中的 'a
等于 Foo<'a>
本身的生命周期,编译器认为该对象是从自身借用的。如你所知,我们不能同时激活一个可变借用和另一个借用(可变或不可变),所以 Rust 阻止我们在 f
上进行可变借用,而 f
本身保留了一个主动借。事实上,由于我们使用了一个通过可变引用获取 self
的方法,Rust 假设 Foo<'a>
存储了一个可变引用,尽管事实并非如此,因为 Rust 只查看签名来确定借用(这是为了确保将私有字段从 &'a T
更改为 &'a mut T
不会对您造成借用检查失败,如果您正在开发库,对您的用户也是如此)。由于对象的类型永远不会改变,因此 Foo<'a>
在其剩余生命周期内被锁定。
现在,你能做什么?显然,您不能在结构中使用 Vec<&'a Subscription<'a>>
。 HashMap
提供了一个 values()
iterator, but it enumerates the values in an unspecified order, so it won't help you if you want to enumerate the values in the order in which they were added. Instead of using borrowed pointers, you could use Rc
:
use std::collections::HashMap;
use std::rc::Rc;
#[derive(Eq, PartialEq, Hash)]
struct SubscriptionKey;
struct Subscription<'a>(&'a ());
struct Foo<'a> {
subscriptions_map: HashMap<SubscriptionKey, Rc<Subscription<'a>>>,
subscriptions: Vec<Rc<Subscription<'a>>>,
}
impl<'a> Foo<'a> {
fn add(&mut self, id: SubscriptionKey, item: Subscription<'a>) {
let item = Rc::new(item);
self.subscriptions_map.insert(id, item.clone());
self.subscriptions.push(item);
}
}
fn main() {
let subscription_data = &();
let mut f = Foo {
subscriptions_map: HashMap::new(),
subscriptions: Vec::new(),
};
f.add(SubscriptionKey, Subscription(subscription_data));
}