【问题标题】:How to determine if passing an argument to a function call will do a move, a copy, a ref or a mut ref?如何确定将参数传递给函数调用是否会执行移动、复制、引用或 mut 引用?
【发布时间】:2022-12-12 16:05:41
【问题描述】:

我在生锈的第一天,来自 php

我正在开发一些基本的控制台程序以获得对所有权的信心。

有没有办法确定将参数传递给函数调用是否会执行移动、复制、引用或 mut 引用?

我指的是结构s only,由我自己定义,暂时。

我指的是不返回值的函数。这对我来说是一个全新的话题

对于我现在能理解的

mystruct: &mut StructureOfMine

明确是一个可变引用,所以如果在函数中我改变了 mystruct 的一个字段,它将反映在外面

mystruct: &StructureOfMine

明确是一个非可变引用,所以在函数中我不能改变 mystruct 的字段

mystruct: StructureOfMine

如果 StructureOfMine 未实现复制,则移动 mystruct。我可以更改mystruct,但调用者不能再使用 mystruct。

如果 StructureOfMine 正在实施复制,则复制 mystruct。我可以更改mystruct;调用者使用其原始的 mystruct 副本,但对此副本的更改不会还原到原始副本中

问题

你能证实/编辑我对这些案例的理解吗?

有没有办法确定是否复制或移动了某些内容?

【问题讨论】:

  • 好像你回答了你自己的问题,Copy types 被复制,所有其他类型都被移动。 & 是不可变引用,&mut 是可变引用。
  • 无法确定某些内容是否已被复制或移动,因为这对任何人都没有影响;既不是传递给它的函数,也不是外部函数。
  • @realtebo “弄清楚”的方法是对其进行编程并查看它是否编译。如果它编译,它就是一个副本,因为稍后使用移动的对象是一个编译错误。你不能在 Rust 中导致未定义的行为,这都是编译器错误。
  • 也许您来自 C++ 背景,您必须积极确保不重复使用移动的对象;这在 Rust 中不是问题,因为实际上不可能偶然做到这一点。
  • @Finomnis,没有 C++ 经验;我正在学习 Rust 正是因为我不知道 C 和 C++,我想学习一些东西 <> PHP

标签: rust ownership


【解决方案1】:

由于@BlackBeans 已经回答了你的大部分问题,我将讨论“我如何检测复制与移动”。

最后实际使用哪一个并不重要。 Rust 在内部解决了这个问题。

对程序员来说唯一的区别是移动的对象在移走后就不能再使用了。然而,这不是什么大问题,因为这不是程序员必须自己确保的事情;这样做错误实际上是一个编译器错误。这就是借用检查器的美妙之处。

在这里,看下面的代码:

#[derive(Debug, Copy, Clone)]
struct StructWithCopy;

fn take_object(obj: StructWithCopy) {
    println!("Taken: {:?} (Address: {:p})", obj, &obj);
}

fn main() {
    let obj = StructWithCopy;
    take_object(obj);
    println!("Original: {:?} (Address: {:p})", obj, &obj);
}
Taken: StructWithCopy (Address: 0x7fff9ced8478)
Original: StructWithCopy (Address: 0x7fff9ced8508)

在这里,使用了一个副本;简单地因为您可以在将对象传递给函数后访问该对象。

如果该对象不可复制,则会导致编译器错误:

#[derive(Debug, Clone)]
struct StructWithoutCopy;

fn take_object(obj: StructWithoutCopy) {
    println!("Taken: {:?} (Address: {:p})", obj, &obj);
}

fn main() {
    let obj = StructWithoutCopy;
    take_object(obj);
    println!("Original: {:?} (Address: {:p})", obj, &obj);
}
error[E0382]: borrow of moved value: `obj`
  --> src/main.rs:11:48
   |
9  |     let obj = StructWithoutCopy;
   |         --- move occurs because `obj` has type `StructWithoutCopy`, which does not implement the `Copy` trait
10 |     take_object(obj);
   |                 --- value moved here
11 |     println!("Original: {:?} (Address: {:p})", obj, &obj);
   |                                                ^^^ value borrowed here after move
   |
   = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)

这就是 Rust 编程的美妙之处;您可以确信,当您的代码编译时,其中没有未定义的行为,例如双重释放、使用未初始化的内存、缓冲区溢出等。

不要想太多,享受你的编译器提供的信心吧:)

【讨论】:

    【解决方案2】:

    您已经了解了这些情况,但似乎您没有看到大局,因为它们都是由一个非常简单的规则处理的,即:当您在函数中传递一个值时,它会被移动,这意味着它被复制到函数期望的新位置。此外,Rust 的所有权模型现在假定变量由被调用函数拥有,不再由调用者拥有。请记住,此规则适用于任何类型。让我们看看它如何适用于您提到的特定情况。

    实现Copy的类型

    让我们从一个基本的开始。 Copy 只是一个标记特征,它告诉编译器“忘记所有权转移”,也就是说,如果我移动一个值,这并不意味着我不再拥有它(至少,从所有权的角度来看)观点;Copy 也暗示其他事情)。

    这实际上很自然:假设我有一个数字,我给你那个数字(通过复制它,这是物理移动内存的唯一方法)。是不是意味着我的号码版本失效?不,这就是为什么 Rust 中所有数字类型都实现 Copy 的原因。

    另一方面,不实现 Copy 的类型例如是向量。这是因为,粗略地说,矢量是指向内存区域的指针,该区域也由拥有该矢量的任何人“拥有”,并且在删除该矢量时被释放。这意味着,当我向你移动一个向量时,我只复制那个指针,不是整个分配的内存(无论如何都不应该移动)。这使无效我的vector 的版本,因为不这样做意味着在内存中分配的数据,由胖指针拥有,现在有两个所有者(这是 Rust 的所有权模型不允许的)。这就是为什么Vec&lt;_&gt;: !Copy请注意,如果您还想复制整个分配的内存,为了保持我的版本有效,您必须显式 clone 它,这是可能的,因为 Vec&lt;T&gt;: Clone if T: Clone

    借用

    现在,借用的情况非常简单。对于任何类型T&amp;T: Copy。就这么简单!当我借用我拥有的数据时,我首先要自己创建一个借用(我拥有的),然后我通过它,这只是复制指针,我的借用仍然有效。

    请注意,这是可能的,因为不可能(至少,让我们假设它是为了简单起见)修改借位后面的值,这使得 &amp;T: Copy 听起来。

    此外,如果仍然有可能有人在某处借用它,我自己将无法访问拥有的数据。

    可变借用

    可变借用与借用非常相似,只是它不实现 Copy(也不实现 Clone,因此您不能显式地实现 clone)。这意味着当你第一次创建一个你拥有的可变借用,然后通过时,它仍然被复制,但你的版本变得无效(因为你给了所有权)。

    此外,您不能创建借用(可变或不可变)或访问拥有的数据,因为可变借用仍然存在。

    【讨论】:

      猜你喜欢
      • 2023-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-04
      • 2016-06-19
      • 1970-01-01
      • 2011-01-31
      • 1970-01-01
      相关资源
      最近更新 更多