如何解决 RefCell 问题?
How can I work around a RefCell issue?
我有一个包含两个 C 指针和一个 Rust 的结构 HashMap
。
struct MyStruct {
p1: *mut ...,
p2: *mut ...,
hm: Box<HashMap<...>>
}
我的结构作为 Rc<RefCell<MyStruct>>
处理,我有一个像这样调用的 C 函数:
c_call(my_struct.borrow().p1, my_struct.borrow().p2);
C 有一个 Rust 回调,它在需要 my_struct.borrow_mut()
的 c_call
执行期间被调用,但是 my_struct
已经为需要 c_call
借用了19=] 和 p2
,所以我得到 RefCell<T> already borrowed
.
问题是 c_call
无法更改,它需要对 p1
和 p2
以及 my_struct
的某些 borrow_mut
的不可变访问。
这是一个 MCVE:
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem::uninitialized;
use std::os::raw::c_void;
use std::rc::Rc;
struct MyStruct {
p1: *mut c_void,
p2: *mut c_void,
hm: Box<HashMap<String, String>>
}
// c_call can't mutate hm because my_struct is already borrowed
// c_call can't be changed
fn c_call(_p1: *mut c_void, _p2: *mut c_void, my_struct: Rc<RefCell<MyStruct>>) {
my_struct.borrow_mut().hm.insert("hey".to_string(), "you".to_string());
}
// call only receives Rc<RefCell<MyStruct>> and need to call c_call
fn call(my_struct: Rc<RefCell<MyStruct>>) {
c_call(my_struct.borrow().p1, my_struct.borrow().p2, my_struct.clone());
}
fn main() {
unsafe {
let my_struct = MyStruct {
p1: uninitialized::<*mut c_void>(), // irrelevant
p2: uninitialized::<*mut c_void>(),
hm: Box::new(HashMap::new())
};
let my_struct = Rc::new(RefCell::new(my_struct));
call(my_struct);
}
}
(Playpen)
我该如何解决这个问题?
您的问题是,在 c_call
调用的参数中调用 borrow()
会借用对象,直到调用完成。如果您将其更改为
let (p1, p2) = {
let x = my_struct.borrow();
(x.p1, x.p2)
};
c_call(p1, p2, my_struct.clone());
然后借用在 c_call
调用之前结束,所以 c_call
也可以 borrow_mut
你的对象。
Rust 可以很好地处理借用单个 struct
的不同字段,但是为了看到这一点,所有字段都必须在同一个词汇项(例如单个函数)中借用。
因此,您至少有两条途径可以继续:
- 重构你的借用,这样你就可以在一个地方分开
struct
,获取对每个字段的引用
- 重构你的借用,以便第一个在需要第二个之前结束
根据您的情况,您选择的解决方案会有所不同:
- 将
struct
分开意味着能够传递对字段的引用而不是整个 struct
- 首先结束借用意味着能够复制借用字段的数据而不是保留引用
我有一个包含两个 C 指针和一个 Rust 的结构 HashMap
。
struct MyStruct {
p1: *mut ...,
p2: *mut ...,
hm: Box<HashMap<...>>
}
我的结构作为 Rc<RefCell<MyStruct>>
处理,我有一个像这样调用的 C 函数:
c_call(my_struct.borrow().p1, my_struct.borrow().p2);
C 有一个 Rust 回调,它在需要 my_struct.borrow_mut()
的 c_call
执行期间被调用,但是 my_struct
已经为需要 c_call
借用了19=] 和 p2
,所以我得到 RefCell<T> already borrowed
.
问题是 c_call
无法更改,它需要对 p1
和 p2
以及 my_struct
的某些 borrow_mut
的不可变访问。
这是一个 MCVE:
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem::uninitialized;
use std::os::raw::c_void;
use std::rc::Rc;
struct MyStruct {
p1: *mut c_void,
p2: *mut c_void,
hm: Box<HashMap<String, String>>
}
// c_call can't mutate hm because my_struct is already borrowed
// c_call can't be changed
fn c_call(_p1: *mut c_void, _p2: *mut c_void, my_struct: Rc<RefCell<MyStruct>>) {
my_struct.borrow_mut().hm.insert("hey".to_string(), "you".to_string());
}
// call only receives Rc<RefCell<MyStruct>> and need to call c_call
fn call(my_struct: Rc<RefCell<MyStruct>>) {
c_call(my_struct.borrow().p1, my_struct.borrow().p2, my_struct.clone());
}
fn main() {
unsafe {
let my_struct = MyStruct {
p1: uninitialized::<*mut c_void>(), // irrelevant
p2: uninitialized::<*mut c_void>(),
hm: Box::new(HashMap::new())
};
let my_struct = Rc::new(RefCell::new(my_struct));
call(my_struct);
}
}
(Playpen)
我该如何解决这个问题?
您的问题是,在 c_call
调用的参数中调用 borrow()
会借用对象,直到调用完成。如果您将其更改为
let (p1, p2) = {
let x = my_struct.borrow();
(x.p1, x.p2)
};
c_call(p1, p2, my_struct.clone());
然后借用在 c_call
调用之前结束,所以 c_call
也可以 borrow_mut
你的对象。
Rust 可以很好地处理借用单个 struct
的不同字段,但是为了看到这一点,所有字段都必须在同一个词汇项(例如单个函数)中借用。
因此,您至少有两条途径可以继续:
- 重构你的借用,这样你就可以在一个地方分开
struct
,获取对每个字段的引用 - 重构你的借用,以便第一个在需要第二个之前结束
根据您的情况,您选择的解决方案会有所不同:
- 将
struct
分开意味着能够传递对字段的引用而不是整个struct
- 首先结束借用意味着能够复制借用字段的数据而不是保留引用