当前位置: 首页 > 知识库问答 >
问题:

rust - Rust闭包返回引用时的规则及与变量本身的关系是什么?

宰父桐
2024-06-03
fn main() {    let mut my_string = String::from("Hello, Rust!");    let mut my_s_ref = &mut my_string;    let  consume_string =   || {        my_s_ref     };    consume_string();    // consume_string();    println!("闭包外部消费字符串: {}", my_string);}
fn main() {    let  my_string = String::from("Hello, Rust!");    let  my_s_ref = & my_string;    let  consume_string =   || {        // my_s_ref.push_str("string");        my_s_ref     };    consume_string();    consume_string();    println!("闭包外部字符串: {}", my_string);}

以上两段代码,一个返回String的可变引用,一个返回不可变引用,为什么返回可变引用的闭包实现了FnOnce,返回不可变引用的闭包只实现了Fn呢?

rust返回引用时,遵循什么规则?和my_s_ref本身有关还是my_string有关?

共有3个答案

钱锦
2024-06-03
fn main() {    let mut my_string = String::from("Hello, Rust!");    let mut my_s_ref = &mut my_string;    let  consume_string =   || {        my_s_ref     };    consume_string();    // consume_string();    println!("闭包外部消费字符串: {}", my_string);}

my_s_ref是可变引用,并且闭包捕获了这个值,就有可能在闭包中进行修改,比如下面在闭包中对捕获的这个可变引用拼接新的内容(如果这个闭包在两个以上的地方同时被调用了就违背了可变引用不能有多个引用原则; 所以闭包对可变引用的捕获只能调用一次):

 let swap = "Formosa".to_string(); my_s_ref.push_str(&swap); my_s_ref 

fn main() {    let  my_string = String::from("Hello, Rust!");    let  my_s_ref = & my_string;    let  consume_string =   || {        // my_s_ref.push_str("string");        my_s_ref     };    consume_string();    consume_string();    println!("闭包外部字符串: {}", my_string);}

上面是对my_s_ref不变引用,闭包是对my_s_ref不变引用,不变引用不能修改my_s_ref的值,不变引用可以有多个,所以这个闭包多次调用没有问题

缪茂勋
2024-06-03

closure 都是 FnOnce ,Fn 也是 FnOnce

可以被多次调用的会是 FnMutFnMut 也是 FnOnce

可以被多次调用并且不改变“状态”的是 FnFn 既是 FnMut ,也是 FnOnce

注意 &String 可以 Copy,但是 &mut String 不行。所以第一个 my_s_ref 被 Move 了,于是不能多次调用。它就只能是 FnOnce 。但是后一个里面 my_s_ref 可以 Copy ,所以可以反复调用。

closures

The way a closure captures and handles values from the environment affects which traits the closure implements, and traits are how functions and structs can specify what kinds of closures they can use. Closures will automatically implement one, two, or all three of these Fn traits, in an additive fashion, depending on how the closure’s body handles the values:

  1. FnOnce applies to closures that can be called once. All closures implement at least this trait, because all closures can be called. A closure that moves captured values out of its body will only implement FnOnce and none of the other Fn traits, because it can only be called once.
  2. FnMut applies to closures that don’t move captured values out of their body, but that might mutate the captured values. These closures can be called more than once.
  3. Fn applies to closures that don’t move captured values out of their body and that don’t mutate captured values, as well as closures that capture nothing from their environment. These closures can be called more than once without mutating their environment, which is important in cases such as calling a closure multiple times concurrently.
贺博厚
2024-06-03

Rust中闭包返回引用时的规则主要涉及到闭包捕获环境的方式以及Rust的借用规则。

首先,我们需要理解Rust中的借用规则:

  • 不可变引用(&T)可以有多个,只要它们不重叠。
  • 可变引用(&mut T)只能有一个,并且它独占其引用的数据。

对于你的两段代码,我们可以分别进行分析:

第一段代码

fn main() {    let mut my_string = String::from("Hello, Rust!");    let mut my_s_ref = &mut my_string;    let consume_string = || {        my_s_ref     };    consume_string();    // consume_string(); // 这里如果取消注释会导致编译错误    println!("闭包外部消费字符串: {}", my_string);}

在这段代码中,consume_string闭包捕获了my_s_ref的可变引用。由于my_s_ref是一个可变引用,这意味着consume_string闭包拥有对my_string的独占访问权限。因此,这个闭包只能被调用一次(FnOnce),因为每次调用都会返回一个可变引用,这会导致所有权和借用问题。

当你试图再次调用consume_string()时(即使注释掉了),Rust编译器会阻止你,因为它会尝试再次返回同一个可变引用,这违反了Rust的借用规则。

第二段代码

fn main() {    let my_string = String::from("Hello, Rust!");    let my_s_ref = &my_string;    let consume_string = || {        // my_s_ref.push_str("string"); // 这会编译失败,因为my_s_ref是不可变的        my_s_ref     };    consume_string();    consume_string();    println!("闭包外部字符串: {}", my_string);}

在这段代码中,consume_string闭包捕获了my_s_ref的不可变引用。由于my_s_ref是不可变的,它可以被多次返回,不会违反Rust的借用规则。因此,这个闭包实现了Fn trait,这意味着它可以被多次调用。

规则总结

Rust中闭包返回引用时的规则主要基于以下几点:

  1. 如果闭包捕获了环境的可变引用,则闭包只能被调用一次(实现FnOnce)。
  2. 如果闭包捕获了环境的不可变引用,则闭包可以被多次调用(实现Fn)。
  3. 闭包返回的引用必须遵守Rust的借用规则,即在同一时间不能有多个可变引用指向同一数据,也不能有可变引用和不可变引用同时指向同一数据(除非是不可变引用指向同一数据)。

这些规则与my_s_ref本身有关,因为它决定了闭包捕获的是可变引用还是不可变引用。而my_string则是被引用的数据,它的可变性(mutable/immutable)影响了你可以如何通过my_s_ref来访问它。

 类似资料:
  • 我正在学习/试验 Rust,在我发现的这门语言的所有优雅中,有一个特点让我感到困惑,似乎完全不合适。 Rust在进行方法调用时会自动取消对指针的引用。我做了一些测试来确定确切的行为: (游乐场) 所以,似乎或多或少: 编译器将插入调用方法所需的尽可能多的解引用运算符 编译器在解析使用<code>声明的方法时 确切的自动取消引用规则是什么?有人能给出这样一个设计决策的正式理由吗?

  • 在中引用会调用闭包,只要在垃圾回收器周围保留就不会清理。问题是--会发生什么?即使它没有被引用,它也是由那个闭包持有的吗?垃圾回收器是否会看到没有引用并将其清理掉?还是只要我坚持,就会与一起存在?(理想的答案是引用ECMA规范。)

  • JNI文档描述了从JNI返回的对象的资源管理规则。下面是一段很好的概述: JNI 将本机代码使用的对象引用分为两类:本地引用和全局引用。本地引用在本机方法调用期间有效,并在本机方法返回后自动释放。全局引用在被显式释放之前一直有效。 对象作为本地引用传递给本机方法。JNI函数返回的所有Java对象都是本地引用。JNI允许程序员从局部引用创建全局引用。期望Java对象的JNI函数接受全局和局部引用。本

  • 我一直在网上读锈书,我已经达到4.1,操作员和过载。我注意到将其定义为,并在特征中单独定义了。 我理解了这里的情况:函数将左侧接受为,将右侧接受为泛型参数类型。 我想知道为什么输出类型是用别名定义的,而不是另一个泛型(例如)?这只是一个惯例,还是有一个特定的原因?

  • 我不明白为什么println是给我0。有什么想法吗? 这是游乐场的链接。https://play.rust-lang.org/?version=stable

  • 作为学习Rust的一个简单练习,我决定实现一个简单的二进制搜索: 在构建时,我得到了这个我不理解的错误。什么是类型?变量mid始终是usize,但即使使用cast,我也会遇到这个编译错误。