【问题标题】:What is the difference between dereferencing a raw pointer to a String and a raw pointer to an i32?取消引用指向 String 的原始指针和指向 i32 的原始指针有什么区别?
【发布时间】:2018-06-01 11:14:33
【问题描述】:
fn func(s: *mut String, a: *mut i32) -> usize {
    println!("{}", unsafe { *s });
    println!("{}", unsafe { *a });

    unsafe { (*s).len() }
}

fn main() {
    let mut s = String::from("hello");
    let mut a = 10;

    func(&mut s, &mut a);
}

上面的代码失败并出现错误:

error[E0507]: cannot move out of dereference of raw pointer
 --> src/main.rs:2:29
  |
2 |     println!("{}", unsafe { *s });
  |                             ^^ cannot move out of dereference of raw pointer

为什么String 而不是i32?为什么抱怨“搬家”?

【问题讨论】:

  • 为什么抱怨“移动”——如果你不知道 Rust 中的“移动”是什么意思,那么你可能不应该使用还没有原始指针。回去重读整本入门书The Rust Programming Language
  • 您可以使用std::ptr::read 复制出原始字符串数据,但这很容易导致未定义的行为。

标签: pointers rust borrow-checker ownership-semantics


【解决方案1】:

为什么String 会发生这种情况而不是i32?

Rust 中的基本整数类型(实际上还有许多其他类型)实现了the Copy trait。它们具有“复制语义”,而不是“移动语义”。这里没有所有权变更......您正在复制价值。 String 没有实现 Copy 特征,因此此绑定具有“移动语义”。

这不是原始指针所独有的,也与它们的可变性无关。 This example shows 不可变引用可能会发生这种情况:

fn func(s: &String, a: &i32) {
    let _x = *s;
    let _x = *a;
}

为什么抱怨“搬家”?

这样做是因为您试图将所有权移出unsafe 块。只要您对此不关心,那么您就需要在unsafe 块中包含“移动”,这样编译器就可以让您在脚下开枪。因此,如果您重组代码以便不移出 unsafe 块之外,代码将编译:

unsafe {
    println!("{}", *s);
}

Here it is running in the playground.

不过,要重申 Shepmaster 在对您的问题的评论中的观点......如果“移动”这个词对您来说听起来很陌生,那么您首先不应该使用原始指针/unsafe 块,而应该使用头回到 Rust 的可用文档来理解这个概念.. 因为它是一个核心。

【讨论】:

  • 所以在调用 println!() 时存在所有权转移?我的意思是为什么它适用于 `let x = String::from ("hello"); println!("{}", x); println!("{}", x); ` .我有点困惑为什么第二个 println!() 成功了?您还能否详细说明“它这样做是因为您试图将所有权移出不安全的块”。请忍受我的愚蠢问题
  • @soupybionics println! 没有所有权(in fact, it auto-references its arguments),但该值必须从块内部移动到外部。 Here's an example of the same problem without any unsafe. {} 块通过将值 *s 变为临时值来强制移动,就像 unsafe 块所做的那样。 println!("{}", *s); 会工作。 (但println!("{}", s) 也是如此。)
  • @trentcl,谢谢!我仍然很不关注它。 unsafe { *s; 之间有什么区别? } 和不安全的 { println!("{}", *s ); }
  • @soupybionics 不同之处在于println! 自动引用它的参数,所以*s 前面有一个隐含的&。我链接的第一个问答解释了这一点。
  • 非常感谢 trentcl!我想我现在明白了要点。基本上,代码let a = String::from("abc"); a; println("{}", a); 触发移动(所有权转移),导致println! 出现预期错误。所以在unsafe { *s; } 的情况下,有一个临时的移动(再次转让所有权),就像上面的“abc”示例一样。而已。我希望我理解正确:)
猜你喜欢
  • 2013-05-21
  • 2021-11-15
  • 1970-01-01
  • 2021-11-05
  • 1970-01-01
  • 2021-01-13
  • 1970-01-01
  • 2010-12-26
  • 1970-01-01
相关资源
最近更新 更多