何时在Rust中使用ref?

9

我注意到了Microsoft的Rust初学者指南中的代码:

fn count_letters(text: &str) -> usize {
    text.chars().filter(|ref c| c.is_alphabetic()).count()
}

显然,在没有 ref 的情况下,代码仍然可以正常工作。

那么这里为什么使用了 ref?我应该在什么时候使用 ref?这里是否有任何惯用的编程实践?


6
在过去,ref 是必要的。今天,你很可能永远不会遇到必须使用它的情况。它是一个遗留物。 - Denys Séguret
4
我不太清楚该如何回答,除了复制并粘贴这本书中优秀的章节: https://doc.rust-lang.org/1.33.0/book/ch18-03-pattern-syntax.html#legacy-patterns-ref-and-ref-mut - Denys Séguret
@DenysSéguret TIL - 我其实不知道 ref 现在被认为是遗留的了!我理解替代方法就是一开始就使用借用,例如 if let Some(inner) = &option 而不是 if let Some(ref inner) = option。我怀疑在问题中引用 ref 在 Rust 1.0 版本之后从未必要,因为 char 是可复制的,并且你总是可以调用拥有值的方法。 - user4815162342
2
@Masklinn 看起来作者本意是写 .filter(|&c| ...)(当使用需要手动比较 c 的闭包调用 filter 时,这种写法经常出现),而不小心写成了 ref - user4815162342
在某些情况下,ref 仍然是必要的。我不同意书中将其称为“遗留代码”的说法。(我也不喜欢 match ergonomics 引入的复杂性和混乱,但这是另一回事。) - Sven Marnach
显示剩余4条评论
1个回答

6

引用自Rust Book:

In older versions of Rust, match would assume that you want to move what is matched. But sometimes, that’s not what you wanted. For example:

let robot_name = &Some(String::from("Bors"));

match robot_name {
    Some(name) => println!("Found a name: {}", name),
    None => (),
}

println!("robot_name is: {:?}", robot_name);

Here, robot_name is a &Option<String>. Rust would then complain that Some(name) doesn’t match up with &Option<T>, so you’d have to write this:

let robot_name = &Some(String::from("Bors"));

match robot_name {
    &Some(name) => println!("Found a name: {}", name),
    None => (),
}

println!("robot_name is: {:?}", robot_name);

Next, Rust would complain that name is trying to move the String out of the option, but because it’s a reference to an option, it’s borrowed, and so can’t be moved out of. This is where the ref keyword comes into play:

let robot_name = &Some(String::from("Bors"));

match robot_name {
    &Some(ref name) => println!("Found a name: {}", name),
    None => (),
}

println!("robot_name is: {:?}", robot_name);

The ref keyword is like the opposite of & in patterns; this says “please bind name* to be a &String, don’t try to move it out." In other words, the & in &Some is matching against a reference, but ref creates a reference. ref mut is like ref, but for mutable references.

Anyway, today’s Rust doesn’t work like this. If you try to match on something borrowed, then all of the bindings you create will attempt to borrow as well. This means that the original code works as you’d expect.

Because Rust is backwards compatible, we couldn’t remove ref and ref mut, and they’re sometimes useful in obscure situations, where you want to partially borrow part of a struct as mutable and another part as immutable. But you may see them in older Rust code, so knowing what they do is still useful.


*这是一项小更正,书中应该使用 ref 而非 name


对我来说,这里的关键部分是ref创建与匹配内容相应的引用,而&则与引用进行比较。 - onlycparra
1
@onlycparra 在模式中的 & 匹配一个引用,但当它绑定到一个标识符时,该值也会被 _解引用_。这就是为什么上面的中间示例会导致移动的原因。 - Peter Hall

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