通过内部可变性模式获得的任何引用(无论是Rc<RefCell<T>>
还是Arc<RwLock<T>>
)都必须在封闭函数返回时被丢弃。当您想使用此引用返回对该RefCell
或RwLock
保护的某些数据的引用时,则不允许这样做。
我认为不允许这样做是正确的:因为我正在使用内部可变性,所以任何与“内部”相关联的引用都应委派给运行时检查器,因此不应将纯引用&T
委托给运行时检查器。暴露在外面。
但是,除了用Rc<RefCell<T>>
包装基础数据之外,我没有找到一个具有相同目的的好选择。
说明问题的代码:
use std::string::String;
use std::sync::{Arc, RwLock};
struct MyStruct {
data: MyData,
}
struct MyData {
val: String,
}
fn main() {
let my_struct = MyStruct {
data: MyData {
val: String::from("hi"),
},
};
let obj = Arc::new(RwLock::new(my_struct));
let data = get_data_ref(&obj);
println!("{}", data);
}
fn get_data_ref(obj: &Arc<RwLock<MyStruct>>) -> &String {
let obj = Arc::clone(obj);
let data = obj.read().unwrap(); //data is created
data.get_data()
} //data dropped here, thus lives not long enough.
impl MyStruct {
fn get_data(&self) -> &String {
self.data.get_val()
}
}
impl MyData {
fn get_val(&self) -> &String {
&self.val
}
}
答案 0 :(得分:2)
这里有2个问题:
让我们按顺序解决它们。
首先,存在一个从let obj = Arc::clone(obj);
继承的生存期问题。
您要将所有生存期引用都锚定到变量,而不是直接从参数中直接读取。
您可以NEVER return a reference to a local variable;所以我们不要。只需删除该行即可:
fn get_data_ref(obj: &Arc<RwLock<MyStruct>>) -> &String {
let data = obj.read().unwrap();
data.get_data()
}
哪个更好了?
第二个问题是内部参考。 RwLock.read()
返回一个 guard ,它将在其析构函数中解锁。作为回报,防护程序仅返回生存期最长的引用,从而确保在防护程序被销毁(并且锁已解锁)之后不会进行任何访问。
不幸的是,没有办法从RwLockReadGuard<'a, MyStruct>
转到某种RwLockReadGuardInner<'a, String>
,这会神奇地授予您对MyStruct
字段的访问权限。
从理论上讲这是可能的,但尚未对此提供支持。
如果您想使其发挥作用...
RwLockReadGuardInner<'a, String>
的思想本质上是:
struct RwLockReadGuardInner<'a, T> {
lock: *const sys::RWLock,
poison: *const poison::Flag,
data: &'a T,
}
impl<'a, T> Drop for RwLockReadGuardInner<'a, T> {
// magic happens
}
然后,您将在map
上执行RwLockReadGuard
操作:
impl<'a, T> RwLockReadGuard<'a, T> {
fn map<F, U>(self, f: F) -> RwLockReadGuardInner<'a, U>
where
F: FnOnce(&T) -> &U,
{
let result = RwLockReadGuardInner {
lock: &self.__lock.inner as *const _,
poison: &self.__lock.poison as *const _,
data: f(unsafe { &*self.__lock.data.get() }),
};
std::mem::forget(self);
result
}
}
我发现有趣的是,稍微更改RwLock
的定义(将inner
和poison
包装在单个结构中)将允许更改RwLockReadGuard
来都引用主场和任何内部场(以其大小加倍为代价)。
如果愿意,可以制定RFC来支持这种情况。 RwLockReadGuard
并不是唯一可能对此有用的方法,大多数内部可变性保护措施将从这种map
操作中受益。