3

阅读rust 教程托管和拥有的指针可以按原样传递给需要借用指针的函数,并在编译时转换为借用指针。

为什么堆栈变量不能以相同的方式传递?什么语法或功能需要显式&运算符传递那个而不是编译器自动转换它?

struct Point {x: float, y: float}

let on_the_stack :  Point =  Point {x: 3.0, y: 4.0};
let managed_box  : @Point = @Point {x: 5.0, y: 1.0};
let owned_box    : ~Point = ~Point {x: 7.0, y: 9.0};

fn compute_distance(p1: &Point, p2: &Point) -> float {
    let x_d = p1.x - p2.x;
    let y_d = p1.y - p2.y;
    sqrt(x_d * x_d + y_d * y_d)
}

compute_distance(&on_the_stack, managed_box);
compute_distance(managed_box, owned_box);

更令人困惑的是 ycombinator 上 kibwen 的引述(我找不到原始引述,但这是引述的引述)

我们仍然有 & 和 &mut,但它们不是指针,它们是引用(在我们的文档中称它们为“借用指针”是我们自己的错)

如果compute_distance正在获取引用并且编译器自动将指针转换为引用,为什么它不能对值做同样的事情?

编辑:由于 pnkfelix 似乎知道他在说什么,我将在此处复制一些对话以便于阅读。

pnkfelix

Rust 的设计者决定在这方面不走 C++ 的道路。这一决定的一个副作用是,当阅读代码的人查看 Rust 中的 f(x, y) 之类的调用时,无需花时间思考“等等,f 如何接受它的参数;如果它改变了 y,将我看到这反映在 f 返回之后?那 x 呢?等等

合资企业

我可能会对引用的性质感到困惑。@var并且~var在 rust 中是指针(尽管它们的行为更像引用) - C++ 引用只是底层的指针吗?

无论如何,在代码可读性方面,我可以将相同的逻辑应用于 C。

变量是值或指针(您只需像在 rust 中那样传递变量:)f(var)或在函数调用中引用(就像您在 rust 中所做的那样:)f(&var)- 我原以为 rust 编译器会识别函数签名并处理这是自动的。我看不到 C 或 C++ 的改进

pnkfelix

一个后续:我写的这一行:“等等,f 如何接受它的参数;如果它改变了 y,我会在 f 返回后看到它的反映吗?x 呢?” 有点滑稽,因为即使像 f(&x, y) 这样的调用也无法修改 x;它必须是 f(&mut x, y)。(并且 x 本身的声明必须是 let mut x = ...,等等。 – pnkfelix 1 小时前

第二个后续:为什么在 Rust 中显式 &x 可能比在 C/C++ 中更重要(而不是让 f(x,y) 隐式地进行借用 &x),是因为借用检查器强制借用遵循某些规则,并且会拒绝编译不符合的代码。当您从借用检查器收到错误时,如果编译器在源代码中指向 &x 或 &mut x 形式的表达式,而不是指向函数调用并说“有一个隐式在这里借。” (当然,这是主观意见。) – pnkfelix 1 小时前

我看到你的回复中有一个后续说明,但我不明白你关于将“相同的逻辑应用于 C”的观点。如果您的意思是 C 而不是 C++,那么在调用需要指针的函数时,您通常必须显式地获取内存地址。如果一个函数需要一个int**,那么有人需要做&Ewhere Eis an l-value type int*。在我看来,这类似于 Rust。(我从 C 中临时记得的主要例外是函数指针;你不需要做&f一个指向 . 的函数指针f。) – pnkfelix 1 小时前

合资企业

在我看来,这类似于 Rust。- 正是我的观点 - 从可读性的角度来看,与普通 C 相比没有太大的改进。因为借用检查器强制借用遵循某些规则......而是指向函数调用并说“这里有一个隐式借用。 " Bingo - 这是我一直在寻找的根本原因之一。如果您发现手动转换的任何其他原因,请随时添加到您的答案中!

4

1 回答 1

4

我认为这样做的目的是让读者清楚地了解何时制作结构的副本以及何时传递对现有结构的引用。(下面有更多内容。)

列出的示例中的 managed-box 和owned-box 已经是参考;它们只是暂时从一种引用(@Point~Point分别)转换为&Point引用。但是,它们都只是引用,并且不会对引用的存储进行复制。


Rust 编译器很可能会根据被调用函数的类型签名推断出何时需要创建引用以及何时需要复制结构。

事实上,这正是 C++ 编译器在看到类似调用时所做的f(x, y):它需要查看 的签名f,例如f(A &a, B b),并据此确定:“好的,我将为 x 参数做一个引用(假设它类型 A)并复制 y 参数(假设它是 B 类型),因为它是按值传递的。”

Rust 的设计者决定在这方面不走 C++ 的道路。这一决定的一个副作用是,当阅读代码的人查看f(x, y)Rust 中的调用时,无需花时间思考“等等,f它的参数是如何接受的;如果它发生变异,我会在返回y后看到它的反映吗? f?怎么样x?等等

  • (当然,人们仍然需要推理从和通过他们自己持有的任何可变或拥有的引用可访问的内存......但我离题了。)xy
于 2013-09-24T00:55:45.180 回答