1

I have a processing loop that needs a pointer to a large lookup table. The pointer is unfortunately triply indirected from the source data, so keeping that pointer around for the inner loop is essential for performance.

Is there any way I can tell the borrow checker that I'm "unborrowing" the state variable in the unlikely event I need to modify the state... so I can only re-lookup the slice in the event that the modify_state function triggers?

One solution I thought of was to change data to be a slice reference and do a mem::replace on the struct at the beginning of the function and pull the slice into local scope, then replace it back at the end of the function — but that is very brittle and error prone (as I need to remember to replace the item on every return). Is there another way to accomplish this?

struct DoubleIndirect {
    data: [u8; 512 * 512],
    lut: [usize; 16384],
    lut_index: usize,
}

#[cold]
fn modify_state(s: &mut DoubleIndirect) {
    s.lut_index += 63;
    s.lut_index %= 16384;
}

fn process(state: &mut DoubleIndirect) -> [u8; 65536] {
    let mut ret: [u8; 65536] = [0; 65536];
    let mut count = 0;
    let mut data_slice = &state.data[state.lut[state.lut_index]..];
    for ret_item in ret.iter_mut() {
        *ret_item = data_slice[count];
        if count % 197 == 196 {
            data_slice = &[];
            modify_state(state);
            data_slice = &state.data[state.lut[state.lut_index]..];
        }
        count += 1
    }
    return ret;
}
4

1 回答 1

3

最简单的方法是确保所有的借用state都是不相交的:

#[cold]
fn modify_state(lut_index: &mut usize) {
    *lut_index += 63;
    *lut_index %= 16384;
}

fn process(state: &mut DoubleIndirect) -> [u8; 65536] {
    let mut ret: [u8; 65536] = [0; 65536];
    let mut count = 0;
    let mut lut_index = &mut state.lut_index;
    let mut data_slice = &state.data[state.lut[*lut_index]..];
    for ret_item in ret.iter_mut() {
        *ret_item = data_slice[count];
        if count % 197 == 196 {
            modify_state(lut_index);
            data_slice = &state.data[state.lut[*lut_index]..];
        }
        count += 1
    }
    return ret;
}

问题基本上是两件事:首先,Rust不会超越函数的签名来找出它的作用。据编译器所知,您的调用也modify_state可能会发生变化state.data,但它不允许这样做。

第二个问题是借用是词法的;编译器查看可能使用借用的代码块。它(目前)不费心尝试减少借用的长度以匹配它们实际活跃的位置。

你也可以玩游戏,例如,使用std::mem::replace拉出state.data一个局部变量,做你的工作,然后replace在你返回之前返回。

于 2016-04-16T18:25:37.910 回答