我正在为Rust编写一个用于Java应用程序的库,我正在尝试将数据从Java代码发送到Rust代码。这个数据由我在Rust侧构建的名为Chunk
的结构组成。我也发送数据来修改这些结构,所以它们需要是可变的。我收到一条错误,指出Chunk
中的HashSet
是不可变的,不应该是这种情况。
#[derive(Eq, PartialEq, Hash)]
struct Chunk {
x: i32,
y: i32,
z: i32,
blocks: [[[i32; 16]; 16]; 16],
}
lazy_static! {
// static mutable list (or at least it should be)
static ref CHUNKS: Mutex<HashSet<Chunk>> = Mutex::new(HashSet::new());
}
#[no_mangle]
pub extern fn add_chunk(cx: i32, cy: i32, cz: i32, c_blocks: [[[i32; 16]; 16]; 16]) {
// create Chunk and put it in the global list
CHUNKS.lock().unwrap().insert(Chunk {x: cx, y: cy, z: cz, blocks: c_blocks});
}
#[no_mangle]
pub extern fn update_block(x: i32, y: i32, z: i32, id: i32) {
let cx: i32 = x / 16;
let cy: i32 = y / 16;
let cz: i32 = z / 16;
let rx: i32 = if x > 0 { x % 16 } else { 16 + (x % 16) };
let ry: i32 = if y > 0 { y % 16 } else { 16 + (y % 16) };
let rz: i32 = if z > 0 { z % 16 } else { 16 + (z % 16) };
for c in CHUNKS.lock().unwrap().iter() {
if c.x == cx && c.y == cy && c.z == cz {
// ERROR: cannot assign to immutable indexed content `c.blocks[..][..][..]`
c.blocks[rx as usize][ry as usize][rz as usize] = id;
}
}
}
我不知道我是否应该使用Vec
或HashSet
,我选择了后者,因为它似乎最简单。
答案 0 :(得分:4)
原始答案不正确 - HashSet
没有iter_mut()
方法:更改哈希表的元素是不安全的,因为它们的位置由其哈希值决定,因此如果值更改,则其哈希值也会发生变化,但由于它是就地修改的,因此它不会再正确地放在哈希表中,并且可能会丢失。
因此,最自然的方法是使用HashMap<(i32, i32, i32), Chunk>
,如@starblue所建议的那样:
lazy_static! {
static ref CHUNKS: Mutex<HashMap<(i32, i32, i32), Chunk>> = Mutex::new(HashMap::new());
}
#[no_mangle]
pub extern fn add_chunk(cx: i32, cy: i32, cz: i32, c_blocks: [[[i32; 16]; 16]; 16]) {
CHUNKS.lock().unwrap().insert((cx, cy, cz), Chunk {x: cx, y: cy, z: cz, blocks: c_blocks});
}
#[no_mangle]
pub extern fn update_block(x: i32, y: i32, z: i32, id: i32) {
let cx: i32 = x / 16;
let cy: i32 = y / 16;
let cz: i32 = z / 16;
let guard = CHUNKS.lock().unwrap();
if let Some(chunk) = guard.get_mut((cx, cy, cz)) {
let rx: i32 = if x > 0 { x % 16 } else { 16 + (x % 16) };
let ry: i32 = if y > 0 { y % 16 } else { 16 + (y % 16) };
let rz: i32 = if z > 0 { z % 16 } else { 16 + (z % 16) };
chunk.blocks[rx as usize][ry as usize][rz as usize] = id;
}
}
此外,使用哈希映射,您无需遍历整个集合即可通过其坐标获取项目。
原始答案如下。
您的代码几乎正确,您只需使用iter_mut()
代替iter()
:
for c in CHUNKS.lock().unwrap().iter_mut()
或者,或者:
for c in &mut *CHUNKS.lock().unwrap()
iter()
返回一个产生不可变引用的迭代器,因此您无法通过它修改任何内容。另一方面,iter_mut()
返回一个迭代器,产生 mutable 引用 - 正是你需要的。
此外,不是直接调用iter_mut()
,而是依靠IntoIterator
实现来引用集合更为惯用:例如,&mut HashSet<T>
通过调用{IntoIterator
来实现iter_mut()
集合{1}},因此for x in &mut hash_set
相当于for x in hash_set.iter_mut()
。此处需要*
,因为unwrap()
不仅会返回包含的值,还会返回一个MutexGuard
,它会反映互斥锁包含的内容。