2016-02-29 31 views
1

我正在爲Rust應用程序編寫Rust庫,並試圖從Java代碼向Rust代碼發送數據。這些數據由我在Rust端構建的稱爲Chunk的結構組成。我還發送數據來修改這些結構,所以它們需要變化。我收到一個錯誤,說HashSet內的Chunk是不可改變的,不應該如此。迭代時無法分配到不可變的索引內容

#[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; 
     } 
    } 
} 

我不知道我是否應該使用一個VecHashSet,我的是後者,因爲它似乎最容易的。

+0

爲什麼不使用'(x,y,z)'來映射塊? – starblue

回答

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()返回一個迭代器,產生可變的引用 - 正是你所需要的。

而不是直接調用 iter_mut()

而且,它是更地道依靠IntoIterator實現爲引用集合:例如,&mut HashSet<T>通過設定呼叫iter_mut()實現IntoIterator,所以for x in &mut hash_set相當於for x in hash_set.iter_mut()。額外的*這裏是必需的,因爲unwrap()不僅返回包含的值,而且還返回MutexGuard,它們將破壞互斥量。

+0

在std :: sync :: mutex :: MutexGuard中,似乎沒有'iter_mut()'std :: collections :: hash :: set :: HashSet > – phase

+0

嗯,確實你是對的。我覺得這是因爲更改散列集項目並不安全 - 如果更改它們,它們的散列碼也可能會發生更改,所以它不會對應於它們在散列集內的位置,並且該項目會丟失。我猜根據starblue的建議,使用(x,y,z)的地圖會更加正確。當我能夠時,我會改變我的答案。 –