通过引用将值传递给函数并通过Box传递它有什么区别?

 heyuntao 发布于 2022-12-07 17:19

将值传递给函数并通过"Box"传递给函数有什么区别:

fn main() {
    let mut stack_a = 3;
    let mut heap_a = Box::new(3);

    foo(&mut stack_a);
    println!("{}", stack_a);

    let r = foo2(&mut stack_a);
    // compile error if the next line is uncommented
    // println!("{}", stack_a);

    bar(heap_a);
    // compile error if the next line is uncommented
    // println!("{}", heap_a);
}

fn foo(x: &mut i32) {
    *x = 5;
}

fn foo2(x: &mut i32) -> &mut i32 {
    *x = 5;
    x
}

fn bar(mut x: Box) {
    *x = 5;
}

为什么heap_a移入函数,但stack_a不是(在调用后的语句中stack_a仍然可用)?println!foo()

取消注释时的错误println!("{}", stack_a);:

error[E0502]: cannot borrow `stack_a` as immutable because it is also borrowed as mutable
  --> src/main.rs:10:20
   |
8  |     let r = foo2(&mut stack_a);
   |                       ------- mutable borrow occurs here
9  |     // compile error if the next line is uncommented
10 |     println!("{}", stack_a);
   |                    ^^^^^^^ immutable borrow occurs here
...
15 | }
   | - mutable borrow ends here

我认为这个错误可以通过参考生命周期来解释.在的情况下foo,stack_a(在main功能)被移动到功能foo,但是编译器发现该函数的自变量的生存期foo,x: &mut i32在的结尾处结束foo.因此,让我们使用变量stack_amain函数后foo返回.在这种情况下foo2,stack_a也移动到函数,但我们也返回它.

为什么heap_a结尾的生命没有结束bar

3 个回答
  • 传递盒装值时,您将完全移动该值.你不再拥有它,你传递给它的东西.对于任何类型都不是这样的Copy(普通的旧数据只能是memcpy'd,堆分配当然不能).这就是Rust的所有权模型的工作原理:每个对象都只在一个地方拥有.

    如果你想改变盒子的内容,你应该传入&mut i32而不是整体Box<i32>.

    实际上,Box<T>它只对递归数据结构很有用(因此它们可以表示而不是无限大小)以及大型类型的非常偶然的性能优化(如果没有测量,您不应该尝试这样做).

    为了&mut i32摆脱a Box<i32>,对可以解除引用的框进行可变引用,即&mut *heap_a.

    2022-12-11 02:04 回答
  • 传值始终是副本(如果涉及的类型是"微不足道的")或移动(如果不是).Box<i32>不可复制,因为它(或至少其中一个数据成员)实现Drop.这通常是针对某种"清理"代码完成的.A Box<i32>是"拥有指针".它是它所指向的唯一所有者,这就是为什么它"感到负责"来释放i32drop功能中的记忆.想象一下如果你复制了一个会发生什么Box<i32>:现在,你将有两个Box<i32>实例指向同一个内存位置.这将是不好的,因为这将导致双重自由错误.这就是为什么bar(heap_a) 移动Box<i32>情况下进入bar().这样,堆分配的所有者总是不止一个i32.这使得管理内存非常简单:谁拥有它,最终释放它.

    不同之foo(&mut stack_a)处在于您没有stack_a按值传递.你只是foo() stack_a以一种foo()能够改变它的方式"借出" .什么foo()得到的是一个借来的指针.当执行返回时foo(),stack_a仍然存在(并且可能通过修改foo()).您可以将其视为stack_a返回其拥有的堆栈帧,因为foo()它只是借用了一段时间.

    似乎让你困惑的部分是通过取消注释最后一行

    let r = foo2(&mut stack_a);
    // compile error if uncomment next line
    // println!("{}", stack_a);
    

    你实际上并没有测试是否stack_a被移动了.stack_a还在那里.编译器根本不允许您通过其名称访问它,因为您仍然有一个可变的借用它的引用:r.这是我们对内存安全所需的规则之一:如果我们也允许更改内存位置,则只能有一种访问内存位置的方法.在这个例子中r是一个可变的借用引用stack_a.所以,stack_a仍然被认为是可变借来的.访问它的唯一方法是通过借用的引用r.

    使用一些额外的花括号,我们可以限制借用引用的生命周期r:

    let mut stack_a = 3;
    {
       let r = foo2(&mut stack_a);
       // println!("{}", stack_a); WOULD BE AN ERROR
       println!("{}", *r); // Fine!
    } // <-- borrowing ends here, r ceases to exist
    // No aliasing anymore => we're allowed to use the name stack_a again
    println!("{}", stack_a);
    

    在结束括号之后,再次只有一种方式访问​​内存位置:名称stack_a.这就是编译器允许我们使用它的原因println!.

    现在您可能想知道,编译器如何知道r实际指的是stack_a什么?它是否分析了它的实施foo2?不,没有必要.功能签名foo2足以得出这个结论.它的

    fn foo2(x: &mut i32) -> &mut i32
    

    这实际上是短暂的

    fn foo2<'a>(x: &'a mut i32) -> &'a mut i32
    

    根据所谓的"终身省略规则".这个签名的含义是:foo2()是一个函数,它将一个借来的指针带到某个函数,并将一个借来的指针i32返回到一个i32相同的i32(或至少是原始的"部分" i32),因为相同的生命周期参数用于返回类型.只要你坚持那个返回值(r),编译器会考虑stack_a可变地借用.

    如果您对我们为什么需要在某些内存位置同时禁止混叠和(潜在)突变感兴趣,请查看Niko的精彩演讲.

    2022-12-11 02:06 回答
  • 传递引用和"按框"之间的区别在于,在引用案例("lend")中,调用者负责解除对象的释放,但在盒子情况下("移动"),被调用者负责解除分配物体.

    因此,Box<T>对于传递负责解除分配的对象很有用,而引用对于传递对象而不负责解除分配很有用.

    一个简单的例子,展示了这些想法:

    fn main() {
        let mut heap_a = Box::new(3);
        foo(&mut *heap_a);
        println!("{}", heap_a);
    
        let heap_b = Box::new(3);
        bar(heap_b);
        // can't use `heap_b`. `heap_b` has been deallocated at the end of `bar`
        // println!("{}", heap_b);
    } // `heap_a` is destroyed here
    
    fn foo(x: &mut i32) {
        *x = 5;
    }
    
    fn bar(mut x: Box<i32>) {
        *x = 5;
    } // heap_b (now `x`) is deallocated here
    

    2022-12-11 02:13 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有