如何使“复制”值在使用后无法访问?

How do I make a `Copy` value inaccessible after use?

提问人:Incömplete 提问时间:8/16/2023 最后编辑:user3840170Incömplete 更新时间:8/17/2023 访问量:88

问:

struct S {
    foo: i32,
    // ... other fields
}


fn f(s: S) {
    // transform foo
    let new_foo = biz_logic(s.foo);
    // from now on, the code should read `new_foo` whenever it needs `s.foo`,
    // how do I "drop" the `s.foo` to prevent misuse?
    // obviously `drop(s.foo)` won't work since it is `Copy`
    // is there any idiom to do this?
    drop(s.foo);

    // OK
    let _ = ... new_foo ...;
    
    // Not OK
    let _ = ... s.foo ...;
}

有没有鲜为人知的 rust 功能可以让你在 Copy 上获得与 drop() 相同的结果?

Rust 成语

评论

2赞 alter_igel 8/16/2023
为什么不就地更新?例如 然后只在事后使用?s.foostd::mem::replace(&mut s.foo, new_foo);s.foo
13赞 alter_igel 8/16/2023
此外,如果在复制后重用 foo 的值是错误的,那么它根本不应该实现。Copy
0赞 ShadowRanger 8/16/2023
@alter_igel:在这种情况下,对于普通函数,在此函数中重用它可能在上下文中不合适,通常不宜这样做(在其他函数中)。i32
1赞 user3840170 8/17/2023
Copy数据应该代表纯粹的信息,无论其来源如何,都可以不加选择地复制和使用。如果您想要一个值只能存在于一个地方的类型,请使用不是 的数据类型,例如使用 newtype 习惯用语:<doc.rust-lang.org/rust-by-example/generics/new_types.html>。Copy

答:

0赞 Filipe Rodrigues 8/16/2023 #1

一种选择是使用可变阴影。但是,你不能 shadow ,所以除非你愿意用 替换它的值并继续使用 ,否则你可以绑定到一个局部变量,然后用 的结果 shadow 它:s.foonew_foos.foos.foofoobiz_logic

fn f(s: S) {
    let foo = s.foo;

    // Use `foo` for whatever
    // ...

    // Shadow `foo` so the old value can't be accessed anymore.
    let foo = biz_logic(foo);

    // Usages of `foo` will always access the new `foo`.
}

评论

0赞 user3840170 8/17/2023
但仍然可以访问。s.foo
1赞 Sam 8/17/2023 #2

如果您对 的其余部分不感兴趣,那么解构和删除将起作用:s

fn f(s: S) {
    let S { foo, .. } = s;
    drop(s);

    // okay
    println!("{foo}");
    
    // not okay
    // println!("{}", s.foo);
}

不幸的是,如果你想得到其余部分(假设其中一些不是),这种方法是行不通的,因为解构分配将导致部分移动,这将使可访问且无法手动删除。sCopys.foos

更好的选择可能是在函数签名和阴影中解构:

fn f(S { foo, .. }: S) {
    let foo = biz_logic(foo);

    // foo is now result of biz_logic and foo from S is unreachable
}

评论

0赞 Filipe Rodrigues 8/17/2023
不幸的是,如果有一个 impl,那么你就无法解构或移出它。(尽管有计划允许这样做)。SDropManuallyDrop
3赞 Silvio Mayolo 8/17/2023 #3

如果要更改现有类型的属性,则需要查找 newtype 模式。我们可以制作一个 newtype 包装器,其行为类似于但没有实现。i32i32Copy

struct MyI32Resource(pub i32);

那么 can 的类型是 ,它遵循 Rust 的默认移动语义,因为我们还没有为它实现。您可以实现任何需要委托给内部字段的类似特征的特征。同样,您也可以将新资源类型取消引用为 .s.fooMyI32ResourceCopyi32MyI32Resourceimpl Derefi32

此方法应仅用于在移动后使用现有值在语义上不合适的情况。也就是说,你真正认为 是某种真正无法复制的资源(Unix 风格的文件描述符就是一个很好的例子;它们只是整数,但从语义上讲,我们希望对它们施加类似资源的约束)。i32

如果你只是担心有人在特定函数中重复使用相同的名称,那么这种方法可能有点矫枉过正,而 Filipe 的方法在这方面更干净。