在函数参数中使用 `ref` 是否等同于自动引用?

5

Rust教程通常建议通过引用传递参数:

fn my_func(x: &Something)

这使得需要在调用处显式地获取该值的引用:
my_func(&my_value).

在IT技术中,通常可以使用模式匹配中使用的ref关键字:

fn my_func(ref x: Something)

我可以通过以下方式调用它:
my_func(my_value)

就内存方面而言,这段代码会如我所愿地工作,还是会在调用my_func之前将my_value复制到栈上,然后获取该副本的引用?

4个回答

14

该值被复制,然后引用该副本。

fn f(ref mut x: i32) {
    *x = 12;
}

fn main() {
    let mut x = 42;
    f(x);
    println!("{}", x);
}

输出:42


8

这两个函数都声明x&Something。不同之处在于前者以引用作为参数,而后者则期望它是一个常规堆栈值。为了说明:

#[derive(Debug)]
struct Something;

fn by_reference(x: &Something) {
    println!("{:?}", x); // prints "&Something""
}

fn on_the_stack(ref x: Something) {
    println!("{:?}", x); // prints "&Something""
}

fn main() {
    let value_on_the_stack: Something = Something;
    let owned: Box<Something> = Box::new(Something);
    let borrowed: &Something = &value_on_the_stack;

    // Compiles:
    on_the_stack(value_on_the_stack);

    // Fail to compile:
    // on_the_stack(owned);
    // on_the_stack(borrowed);

    // Dereferencing will do:
    on_the_stack(*owned);
    on_the_stack(*borrowed);

    // Compiles:
    by_reference(owned); // Does not compile in Rust 1.0 - editor
    by_reference(borrowed);

    // Fails to compile:
    // by_reference(value_on_the_stack);

    // Taking a reference will do:
    by_reference(&value_on_the_stack);
}

由于on_the_stack接受一个值,所以它会被复制,然后副本与形式参数中的模式匹配(在您的示例中是ref x)。匹配将x绑定到对复制值的引用。


1
由于 on_the_stack 接受一个值,因此它会被复制。由于 Something 类型没有实现 Copy,所以它会被移动。最好使用“按值传递”而不是复制/移动。 - jsstuball

5
如果你像这样调用一个函数 f(x),那么 x 总是按值传递。
fn f(ref x: i32) {
    // ...
}

等同于

fn f(tmp: i32) {
    let ref x = tmp;
    // or,
    let x = &tmp;

    // ...
}

即参考完全限于函数调用。

4

如果值类型没有实现Copy,那么你的这两个函数之间的差异就会更加显著和明显。例如,一个Vec<T>不实现Copy,因为那是一项代价高昂的操作,相反,它实现了Clone(需要调用特定方法)。

假设两个方法定义如下:

fn take_ref(ref v: Vec<String>) {}// Takes a reference, ish
fn take_addr(v: &Vec<String>) {}// Takes an explicit reference

take_ref 函数会在引用参数之前尝试复制其值。对于 Vec<T>,这实际上是一个移动操作(因为它不会复制)。这实际上消耗了向量,意味着以下代码将引发编译器错误:

let v: Vec<String>; // assume a real value
take_ref(v);// Value is moved here
println!("{:?}", v);// Error, v was moved on the previous line

然而,当引用是显式的时候,比如在take_addr中,Vec并不会被移动,而是通过引用传递。因此,这段代码可以按预期工作:
let v: Vec<String>; // assume a real value
take_addr(&v);
println!("{:?}", v);// Prints contents as you would expect

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