V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xiaopanzi
V2EX  ›  Rust

一个 second mutable borrow 的错误

  •  
  •   xiaopanzi · 2021-06-23 15:53:11 +08:00 · 1407 次点击
    这是一个创建于 1245 天前的主题,其中的信息可能已经有所发展或是发生改变。

    学 Rust 有点走火入魔了,感觉这是一个简单的 Bug,但还解决不了。代码放在 Rust Playground

    内容其实是设计模式中 Facade Pattern 的实现:这里把无关代码都删了,Tuner 用来调频,Amplifier 是放大器,HomeTheater 中有 Tuner 和 Amplifier 。

    最后在测试的 main:

    let amp = Amplifier::new("Amplifier");
    let tuner = Tuner::new("AM/FM Tuner");
    
    let mut home_theater = HomeTheaterFacade::new(amp, tuner);
    home_theater.listen_radio();
    home_theater.stop_radio();
    }
    

    这样写的话就报错:cannot borrow home_theater as mutable more than once at a time 。当然,我只写 listen_radiostop_radio 就没有问题。


    对比下面的代码,

    #[derive(Default)]
    struct Foo {
        i: i32,
        j: i32,
    }
    impl Foo {
        fn set_i(&mut self, i: i32) {
            self.i = i;
        }
        fn set_j(&mut self, j: i32) {
            self.j = j;
        }
    }
    

    感觉下面的代码和前面的类似,为什么没有 second mutable borrow 的错误?

    let mut foo = Foo::default();
    foo.set_i(10);
    foo.set_j(64);
    
    8 条回复    2021-07-06 15:36:44 +08:00
    hotdogwc
        1
    hotdogwc  
       2021-06-23 16:10:08 +08:00
    因为你 listen_radio() 的 borrow 生命周期和 home_theater 是一样长的,borrow checker 认为你的 mutable borrow 没结束,你调换一下 listen_radio() 和 stop_radio() 的调用顺序就不报错了啊
    hotdogwc
        2
    hotdogwc  
       2021-06-23 16:13:49 +08:00
    并且你 Foo 那个例子,给 set_i 的 mutable borrow 一个和 foo 一样的生命周期,也会报错,跟你的代码情况一样
    xiaopanzi
        3
    xiaopanzi  
    OP
       2021-06-23 16:16:28 +08:00
    @hotdogwc 谢谢大佬。再请问一下,如果想在 listen_radio 里面实现 self.amp.set_tuner(&self.tuner),应该如何指定生命周期?
    xiaopanzi
        5
    xiaopanzi  
    OP
       2021-06-23 16:42:53 +08:00
    @hotdogwc 多谢。我好好研究一下。发现把(引用和生命周期)全部魔改成 Rc<RefCell<Tuner>>,代码就好写多了。
    Jirajine
        6
    Jirajine  
       2021-06-23 17:02:48 +08:00
    根本原因是你创建了一个 self referential struct,你希望 Amplifier<'a>的'a 对应 tunner,但你创建 HomeTheaterFacade<'_>的时候'a 已经被填充为 HomeTheaterFacade 的 lifetime 了。而 safe rust 正常来说是不允许你创建 self referential struct 的,因为一 move 引用就失效了。
    解决办法是要么用 raw pointer,并且保证 strcut 不要 move 。要么用 Pin 、Rc,这两者都需要分配到堆上。
    irytu
        7
    irytu  
       2021-06-23 17:19:50 +08:00 via iPhone
    还是整体代码组织的问题 重构吧 也许你说的用 refcell 去 borrow_mut 也行 不过最好还是把整体的思路改一下
    PTLin
        8
    PTLin  
       2021-07-06 15:36:44 +08:00
    你这个问题其实精简一下是这样。
    ```rust
    let mut a:(Vec<i32>,Option<&Vec<i32>>) = (vec![1], None);
    a.1=Some(&a.0);
    let b=&mut a;
    println!("{:?}", b);
    ```
    第二行过后,只要 a 还活着就持有 a.0 的一个引用,这时候就不能以任何的途径对 a.0 取可变引用。
    所以第三行不会通过编译,因为这在你还对 a.0 有引用的情况下,又对 a.0 取了可变引用(因为使用 a 也能触及到 a.0),相反第三行可以取 a.1 的可变引用。
    a 的两条路径每一条都可以单独操作,但是要是直接操作 a 就相当于直接操作了两个路径。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3256 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 12:39 · PVG 20:39 · LAX 04:39 · JFK 07:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.