在函数参数中,使用ref关键字和使用&符号有什么区别?

6

在这段代码中,sref1sref2是指向s的地址,而且这两个地址是相同的。那么ref&之间有什么区别?

fn main() {
    let s = String::from("hello");
    let sref1 = &s;
    let ref sref2 = s;
    println!("{:p}", sref1);
    println!("{:p}", sref2);

    f1(&s);
    f2(s);
}

fn f1(_s: &String) {
    println!("{:p}", _s);
}

fn f2(ref _s: String) {
    println!("{:p}", _s);
}

在f1和f2中,_s也是字符串的地址,f2会拥有这个地址,但是f2打印的地址与f1打印的地址不同。为什么呢?
3个回答

6
在模式匹配中,& 用于解构借用操作,ref 则是通过引用方式绑定到位置而非值。
换句话说,& 让你可以穿透借用的对象,而 ref 的作用是“获取我正在匹配的对象中这个位置的借用”。

&ref 是互相对应的。

#![feature(core_intrinsics)]

fn main() {
    let x = &false;
    print_type_name_of(x);

    let &x = &false;
    print_type_name_of(x);

    let ref x = &false;
    print_type_name_of(x);
}

fn print_type_name_of<T>(_: T) {
    println!("{}", unsafe { std::intrinsics::type_name::<T>() })
}

输出结果如下所示:
&bool
bool
&&bool

你可以查看以下链接以获取更多信息:,rust-bookrust-usersreddit-ref keyword - Akiner Alkan

3

&适用于rvalue(类型),而ref适用于lvalue(变量名),但它们都执行相同的操作。

ref在模式内部非常有用,因为您只能访问lvalue:

#![feature(core_intrinsics)]

fn print_type<T>(_: T) {
    println!("{}", unsafe { std::intrinsics::type_name::<T>() })
}

fn main() {
    let opt = Some(0);

    match opt {
        Some(ref i) => print_type(i), // &i32
        None => (),
    }
}

然而,现在这个关键词不是很有用,因为模式匹配变得更加“聪明”了。它会理解如果你借用匹配值,那么意味着你想要借用内部的值:
match &opt {
    Some(i) => print_type(i), // &i32
    None => (),
}

因为模式匹配更加“聪明”,是的,更聪明了,但并不完全。我发现自己需要使用ref,而我原本并没有预料到这一点。 - Shepmaster

3
在函数参数中,使用ref关键字和使用&符号有什么区别?
你已经回答了自己的问题:
使用ref将会转移所有权。
变量的所有权被转移到函数中,然后对移动后的变量进行引用。也就是说,
fn f2(ref _s: String) {}

等同于

fn f2(s0: String) {
    let _s = &s0;
    // or 
    // let ref _s = s0;
}

如果您使用&String不建议使用),函数只会获取变量的引用,而不是所有权。

打印出来的地址[...]不同了

是的,因为变量被移动了。当一个变量被移动时,它所在的地址可能会改变。这就是为什么它被称为“移动”。
另请参见:

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