gpt4 book ai didi

reference - 获取&'a T from Option>

转载 作者:行者123 更新时间:2023-11-29 08:30:07 25 4
gpt4 key购买 nike

恐怕这可能是非常基础的,但我自己还没有弄明白。我有这张 map :

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() 返回给定键的 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 的定义 field 。它被定义为 Vec<&'a Subscription<'a>> .满足 'aSubscription<'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 短)。

但是,通过插入 &'a Subscription<'a>Foo<'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);
}
}

此方法编译正常。但是,如果我们试图在 Foo 上调用它我们称之为add()早点,然后 self.subscriptions将包含对曾经在 self.subscriptions_map 中的项目的悬空引用.

所以可变借用在调用add()后仍然存在的原因就是这样,因为'aFoo<'a>等于 Foo<'a> 的生命周期自身,编译器认为对象是从自身借用的。如您所知,我们不能同时激活一个可变借用和另一个事件借用(可变或不可变),因此 Rust 阻止我们在 f 上进行可变借用。同时f本身保留主动借用。事实上,因为我们使用了一个方法,需要 self通过可变引用,Rust 假定 Foo<'a>存储一个可变引用,即使事实并非如此,因为 Rust 只查看签名来确定借用(这是为了确保将私有(private)字段从 &'a T 更改为 &'a mut T 不会导致您和借用检查失败,如果你正在开发一个图书馆,给你的用户)。由于对象的类型永远不会改变,Foo<'a>在其剩余生命周期内被锁定。

现在,你能做什么?显然,您不能有用地拥有 Vec<&'a Subscription<'a>>在你的结构中。 HashMap提供一个 values() 迭代器,但它以未指定的顺序枚举值,因此如果您想按照添加它们的顺序枚举值,它不会帮助您。您可以使用 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));
}

关于reference - 获取&'a T from Option<T<' a>>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36138477/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com