Rust“借用指针”语法

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);

增加了混淆的是来自kibwen在ycombinator上的一句话(我找不到原始引用,但这是引用的引用)

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

如果compute_distance正在获取引用,并且编译器会自动将指针转换为引用,为什么不能对值执行相同的操作?

编辑:由于pnkfelix似乎知道他在谈论什么,因此我将在此复制一些对话以便更容易阅读。

pnkfelix

Rust的设计者决定在这方面不遵循C++的路线。 这种决定的一个副作用是,当某人在Rust中看到类似f(x,y)的调用时,他不需要浪费时间想“等等,f如何获取其参数;如果它改变了y,我是否会在f返回之后看到它?x呢?”等等。

J V

我可能对引用的性质感到困惑。 Rust中的@var~var是指针(虽然它们的行为更像引用),- 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 **,那么某人需要做一个&E,其中E是类型为int *的l-value。对我来说,这似乎类似于Rust。 (我从C中记得的主要例外是函数指针;您不需要做&f来使函数指针指向f。)-pnkfelix 1小时前
这对我来说似乎与Rust类似。-恰好是我的观点-从可读性的角度来看,与普通的C相比没有太大的改进。因为借用检查器强制执行借用遵循某些规则...而不是指向函数调用并说“这里有一个隐式借用”。" Bingo-这是我寻找的潜在原因之一。如果您发现其他手动转换的原因,请随时添加到您的答案中!
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++的路径。这个决定的一个副作用是,当某人在Rust中查看类似f(x, y)这样的调用时,就不需要花时间思考“等等,f如何获取其参数;如果它改变了y,那么在f返回后我是否会看到这一点?x呢?”等等。
  • (当然,仍然需要推理通过任何可变或拥有的引用它们自己持有的xy可达的存储器...但我跑题了。)

我可能对引用的本质感到困惑。在 Rust 中,@var~var 是指针(虽然它们的行为更像是引用)- C++ 引用在底层是否只是指针呢?无论如何,我可以将相同的逻辑应用于 C 以提高代码可读性。变量要么是值或指针(就像在 Rust 中简单地传递变量一样:f(var)),要么在函数调用中被引用(就像在 Rust 中所做的那样:f(&var))- 我本以为 Rust 编译器会识别函数签名并自动处理这个问题。我没有看到比 C 或 C++ 更好的改进。 - J V
一个后续问题:我写的这行代码:“等等,f函数怎么接收它的参数;如果它改变了y,那么在f返回之后,我会看到这种反映吗?x呢?”有点玩笑,因为即使像f(&x, y)这样的调用也无法修改x;它必须是f(&mut x, y)。(并且x本身的声明也必须是let mut x = ...,等等。 - pnkfelix
第二次跟进:在Rust中,显式使用&x比在C/C++中更重要的原因(而不是让f(x,y)隐式地进行借用&x),是因为借用检查器强制执行某些规则,并且会拒绝编译不符合规范的代码。当您从借用检查器获得错误时,如果编译器指向源代码中的&x&mut x形式的表达式,而不是指向函数调用并说“这里有一个隐式借用”,那么这将是更好的用户体验。(当然,这是主观意见。) - pnkfelix
我看到你在回复中有一个后续注释,但我不明白你所说的关于将“相同的逻辑应用于C”的观点。如果你指的是C而不是C ++,那么当调用期望指针的函数时,通常必须显式地获取内存地址。如果一个函数需要int **,那么某人需要做&E,其中E是类型为int *的l-value。对我来说,这似乎类似于Rust。(我从C中记得的主要例外是函数指针; 您不需要执行&f来使函数指针指向f)。 - pnkfelix
这对我来说似乎类似于Rust。从可读性的角度来看,与普通的C相比并没有太大的改进。因为借用检查器强制执行借用遵循某些规则...而不是指向函数调用并说“这里有一个隐式借用”。太对了 - 这是我寻找的根本原因之一。如果您发现手动转换的其他原因,请随时添加到您的答案中! - J V

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接