使用选项<& mut> 2次

时间:2015-03-29 21:38:33

标签: memory-management rust

以下程序编译良好:

fn write_u16(bytes: &mut Vec<u8>, value: u16) {
    bytes.push((value >> 8) as u8);
    bytes.push(value as u8);
}

fn write_u32(bytes: &mut Vec<u8>, value: u32) {
    write_u16(bytes, (value >> 16) as u16);
    write_u16(bytes, value as u16);
}

现在我将字节类型更改为Option&lt;&amp; mut Vec&gt;:

fn write_u16(bytes_opt: Option<&mut Vec<u8>>, value: u16) {
    if let Some(bytes) = bytes_opt {
        bytes.push((value >> 8) as u8);
        bytes.push(value as u8);
    }
}

fn write_u32(bytes_opt: Option<&mut Vec<u8>>, value: u32) {
    write_u16(bytes_opt, (value >> 16) as u16);
    write_u16(bytes_opt, value as u16);
}

该程序现在不编译:

main.rs:10:15: 10:24 error: use of moved value: `bytes_opt`
main.rs:10     write_u16(bytes_opt, value as u16);
                         ^~~~~~~~~
main.rs:9:15: 9:24 note: `bytes_opt` moved here because it has type `core::option::Option<&mut collections::vec::Vec<u8>>`, which is non-copyable
main.rs:9     write_u16(bytes_opt, (value >> 16) as u16);
                        ^~~~~~~~~
error: aborting due to previous error

我真的不明白,为什么我不能两次使用Option,我该如何解决这个问题?

我能想象解决这个问题的唯一方法是:

fn write_u32(bytes_opt: Option<&mut Vec<u8>>, value: u32) {
    if let Some(bytes) = bytes_opt {
        write_u16(Some(bytes), (value >> 16) as u16);
        write_u16(Some(bytes), value as u16);
    } else {
        write_u16(None, (value >> 16) as u16);
        write_u16(None, value as u16);
    }
}

但这不是很好的代码。

2 个答案:

答案 0 :(得分:3)

虽然我怀疑在这种情况下你不应该这样做(已经评论过),但是可以为这种安全的情况重新提供可变引用:

fn write_u32(mut bytes_opt: Option<&mut Vec<u8>>, value: u32) {
    write_u16(bytes_opt.as_mut().map(|x| &mut **x), (value >> 16) as u16);
    write_u16(bytes_opt, value as u16);
}

bytes_opt.as_mut().map(|x| &mut **x)也可以写成match bytes_opt { Some(&mut ref mut x) => Some(x), None => None, }。一个非常令人折服的模式(从左到右阅读:&mut - 取消引用包含的值ref mut - 然后对其进行新的可变引用),但它可以工作并避免所有权问题

答案 1 :(得分:2)

错误消息告诉您关键事项:

  

bytes_opt已移至此处,因为其类型为core::option::Option<&mut collections::vec::Vec<u8>>,且不可复制

您的函数签名表明它将使用参数:

fn write_u16(bytes_opt: Option<&mut Vec<u8>>, value: u16)
//                      ^~~~~~~~~~~~~~~~~~~~

但是,通过使用它,它还消耗可变引用。如果您有其他类型,如Option<u8>Option<&Vec<u8>>,则编译器可以为您插入变量的隐式副本。但是,您不能复制可变引用,因为那时您将拥有可变别名,由于内存安全原因,编译器不允许这样做。

当你只传递&mut Vec<u8>时,编译器能够跟踪引用,并且看到一次只有一个项目具有引用,因此它允许它。但是,当可变引用嵌入另一种类型时,它无法跟踪它。

要真正让它发挥作用,它有点难看,有超过mut个限定词:

fn write_u16(bytes_opt: &mut Option<&mut Vec<u8>>, value: u16) {
    if let Some(ref mut bytes) = *bytes_opt {
        bytes.push((value >> 8) as u8);
        bytes.push(value as u8);
    }
}

fn write_u32(mut bytes_opt: Option<&mut Vec<u8>>, value: u32) {
    write_u16(&mut bytes_opt, (value >> 16) as u16);
    write_u16(&mut bytes_opt, value as u16);
}

@ChrisMorgan提示,我得到了适合您原始API的内容:

fn write_u16(bytes_opt: Option<&mut Vec<u8>>, value: u16) {
    if let Some(bytes) = bytes_opt {
        bytes.push((value >> 8) as u8);
        bytes.push(value as u8);
    }
}

fn write_u32(bytes_opt: Option<&mut Vec<u8>>, value: u32) {
    if let Some(bytes) = bytes_opt {
        write_u16(Some(bytes), (value >> 16) as u16);
        write_u16(Some(bytes), value as u16);
    }
}