我有一个包含一些锁定数据的结构体,实际情况很复杂,但这里有一个最小的例子(或者说我能做的最小的例子):
use std::fmt::Display;
use std::ops::{Index, IndexMut};
use std::sync::Mutex;
struct LockedVector<T> {
stuff: Mutex<Vec<T>>,
}
impl<T> LockedVector<T> {
pub fn new(v: Vec<T>) -> Self {
LockedVector {
stuff: Mutex::new(v),
}
}
}
impl<T> Index<usize> for LockedVector<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
todo!()
}
}
impl<T> IndexMut<usize> for LockedVector<T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
let thing = self.stuff.get_mut().unwrap();
&mut thing[index]
}
}
impl<T: Display> Display for LockedVector<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let strings: Vec<String> = self
.stuff
.lock()
.unwrap()
.iter()
.map(|s| format!("{}", s))
.collect();
write!(f, "{}", strings.join(", "))
}
}
fn main() {
let mut my_stuff = LockedVector::new(vec![0, 1, 2, 3, 4]);
println!("initially: {}", my_stuff);
my_stuff[2] = 5;
println!("then: {}", my_stuff);
let a_mut_var: &mut usize = &mut my_stuff[3];
*a_mut_var = 54;
println!("Still working: {}", my_stuff);
}
我在这里尝试做的是在一个结构体上实现Index
和IndexMut
trait,其中被索引的数据在一个互斥锁后面,我对为什么这是可能的非常模糊的推理是,锁定互斥锁的结果有点像引用,看起来你可以把一个引用Map到另一个引用上,或者以某种方式进行一种引用,该引用 Package 整个锁但仅解引用特定索引。
我不那么模糊的推理是上面的代码编译并运行(注意todo!
)--我能够取回可变引用,并且我假设我没有以非线程安全的方式偷偷通过互斥锁(我尝试测试线程行为,但是在尝试将可变引用引入另一个线程时遇到了其他问题)。
奇怪的问题是,我不能对Index
做同样的事情--没有get_immut()
可以使用,我也没有找到其他方法。我可以从互斥体得到一个可变引用,但不能得到一个不变引用(当然,如果我只有一个不变引用,我就不能得到可变引用)
我的期望是索引将获得一个锁,并且返回的引用(在可变和不可变的情况下)将在它们的生命周期内保持该锁。作为一个额外的好处,如果RwLock
-ed的东西只能为不可变的情况获取/持有读锁,为可变的情况获取/持有写锁,那就太好了。
关于我为什么要这么做的背景:我有一个Grid
trait,它被很多不同的代码使用,但是有不同的实现支持,其中一些是线程安全的。我希望把Index
和IndexMut
trait放在它上面,以获得良好的语法。线程通常根本没有对线程安全网格的可变引用,所以IndexMut
trait在那里几乎没有用处。但是我可以看到它在安装过程中或者在非线程安全的情况下是有价值的。不可变的Index
行为似乎在任何地方都是有用的。
附加问题:我非常讨厌Display
代码,我怎么才能让它不那么可怕呢?
1条答案
按热度按时间9rnv2umw1#
如果你看一下
get_mut
的文档,你会发现它之所以可能,正是因为一个可变引用确保了它没有其他引用或锁,不幸的是,对你来说,这意味着get_ref
对Mutex
只有通过一个可变引用才可能,这只是一个人为限制的get_mut
。不幸的是,由于
Index
只给你一个共享引用,你不能安全地获得对它的内容的共享引用,所以你不能实现一个Index
,以便它索引到Mutex
后面的东西。