【问题标题】:Cannot borrow self twice in one function call不能在一个函数调用中借用 self 两次
【发布时间】:2021-03-07 03:54:06
【问题描述】:

我是rust 的新手,对函数调用感到震惊。 很抱歉,我无法制作干净的错误示例,因为iced::PickList 的构造函数中出现错误。

问题是——只要我在该调用中只使用一种方法——构建过程和程序就可以工作,但如果我只是将第二个需要的方法的签名更改为不是静态的——我就无法构建和修复 bowwor完全错误。

playground 上的完整工作(构建和启动)示例。

虽然看起来像这样,但通过Self::port_names() 调用,它可以工作。

extern crate iced;
extern crate midir;

use std::cell::RefCell;
use std::error::Error;
use std::io::{stdin, stdout, Write};
use std::option::Option::Some;
use std::sync::mpsc::{channel, Sender};

use iced::{
    button, executor, pick_list, scrollable, Align, Application, Button, Column, Command,
    Container, Element, Length, PickList, Scrollable, Settings, Space, Text,
};
use midir::{Ignore, MidiInput, MidiInputConnection, MidiInputPort, MidiInputPorts};

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
struct Port {
    pub index: usize,
}

struct BigNote {
    note: i32,
    need_init: bool,
    client_name: String,
    scroll: scrollable::State,
    ports_list: pick_list::State<Port>,
    selected_port: Port,

    connection: Option<MidiInputConnection<Sender<Vec<u8>>>>,
}

impl BigNote {
    fn midi_in(&self) -> MidiInput {
        MidiInput::new(&self.client_name.to_string()).unwrap()
    }
    fn ports_names() -> Vec<Port> {  // this is static method
        let mut names: Vec<Port> = Vec::new();
        names.push(Port { index: 0 });
        names.push(Port { index: 1 });
        names.into()
    }
}

impl Application for BigNote {
    type Executor = executor::Default;
    type Message = Message;
    type Flags = ();

    fn new(_flags: ()) -> (BigNote, Command<self::Message>) {
        (
            BigNote {
                note: -1,
                need_init: true,
                client_name: String::from("MyBigNote"),
                connection: Option::None,
                ports_list: pick_list::State::<Port>::default(),
                scroll: scrollable::State::default(),
                selected_port: Port { index: 1 },
            },
            Command::none(),
        )
    }

    fn view(&mut self) -> Element<Message> {
        let pick_list = PickList::new(
            &mut self.ports_list,
            Self::ports_names(),  // here is static method
            Some(self.selected_port.clone()),
            Message::PortSelected,
        );

        let mut content = Scrollable::new(&mut self.scroll)
            .width(Length::Fill)
            .align_items(Align::Center)
            .spacing(10)
            .push(Space::with_height(Length::Units(600)))
            .push(Text::new("Which is your favorite String?"))
            .push(pick_list);

        content = content.push(Space::with_height(Length::Units(600)));

        Container::new(content)
            .width(Length::Fill)
            .height(Length::Fill)
            .center_x()
            .center_y()
            .into()
    }
}

如果我只是替换方法签名——问题从«不可变借用不能与可变一起执行»开始,直到«临时资源不能从函数返回»。

extern crate iced;
extern crate midir;

use std::cell::RefCell;
use std::error::Error;
use std::io::{stdin, stdout, Write};
use std::option::Option::Some;
use std::sync::mpsc::{channel, Sender};

use iced::{
    button, executor, pick_list, scrollable, Align, Application, Button, Column, Command,
    Container, Element, Length, PickList, Scrollable, Settings, Space, Text,
};
use midir::{Ignore, MidiInput, MidiInputConnection, MidiInputPort, MidiInputPorts};

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
struct Port {
    pub index: usize,
}

struct BigNote {
    note: i32,
    need_init: bool,
    client_name: String,
    scroll: scrollable::State,
    ports_list: pick_list::State<Port>,
    selected_port: Port,

    connection: Option<MidiInputConnection<Sender<Vec<u8>>>>,
}

impl BigNote {
    fn midi_in(&self) -> MidiInput {
        MidiInput::new(&self.client_name.to_string()).unwrap()
    }
    fn ports_names(&self) -> Vec<Port> { // here is change
        let mut names: Vec<Port> = Vec::new();
        names.push(Port { index: 0 });
        names.push(Port { index: 1 });
        names.into()
    }
}

impl Application for BigNote {
    type Executor = executor::Default;
    type Message = Message;
    type Flags = ();

    fn new(_flags: ()) -> (BigNote, Command<self::Message>) {
        (
            BigNote {
                note: -1,
                need_init: true,
                client_name: String::from("MyBigNote"),
                connection: Option::None,
                ports_list: pick_list::State::<Port>::default(),
                scroll: scrollable::State::default(),
                selected_port: Port { index: 1 },
            },
            Command::none(),
        )
    }

    fn view(&mut self) -> Element<Message> {
        let pick_list = PickList::new(
            &mut self.ports_list,
            self.ports_names(),  // and here
            Some(self.selected_port.clone()),
            Message::PortSelected,
        );

        let mut content = Scrollable::new(&mut self.scroll)
            .width(Length::Fill)
            .align_items(Align::Center)
            .spacing(10)
            .push(Space::with_height(Length::Units(600)))
            .push(Text::new("Which is your favorite String?"))
            .push(pick_list);

        content = content.push(Space::with_height(Length::Units(600)));

        Container::new(content)
            .width(Length::Fill)
            .height(Length::Fill)
            .center_x()
            .center_y()
            .into()
    }
}

请帮助我了解为什么会发生这种情况以及如何避免此类问题。

[编辑]

我觉得,我仍然没有得到 rust 中变量\局部变量的基本概念,就像我将 view 函数更改为这样:

fn view(&mut self) -> Element<Message> {
        let port_names = self.ports_names(); // method call now here
        let pick_list = PickList::new(
            &mut self.ports_list,
            port_names, // here only variable
            Some(self.selected_port.clone()),
            Message::PortSelected,
        );

        let mut content = Scrollable::new(&mut self.scroll)
            .width(Length::Fill)
            .align_items(Align::Center)
            .spacing(10)
            .push(Space::with_height(Length::Units(600)))
            .push(Text::new("Which is your favorite String?"))
            .push(pick_list);

        content = content.push(Space::with_height(Length::Units(600)));

        Container::new(content)
            .width(Length::Fill)
            .height(Length::Fill)
            .center_x()
            .center_y()
            .into()
    }

成功了。 我知道原因是 - 我将直接方法调用移至本地可行,但我自己看不出有什么区别......

【问题讨论】:

    标签: rust borrow-checker


    【解决方案1】:

    只要有复合表达式,如结构构造函数或函数调用,

            let pick_list = PickList::new(
                &mut self.ports_list,
                self.ports_names(),  // and here
                Some(self.selected_port.clone()),
                Message::PortSelected,
            );
    

    对它的评估以一种非常具体的方式进行:首先,每个参数表达式都被评估,按照它们在源代码中的写入顺序,它们的值临时存储在堆。然后,一旦所有参数值都准备好,就调用该函数。

    在这种情况下:

    1. &amp;mut self.ports_list 首先被评估并存储为临时值。现在,self.ports_list 是可变借用的。

    2. self.ports_names() 被第二次评估 - 但 self.ports_list 是可变借用的,调用 ports_names 需要 所有self 的不可变借用,这是不允许的,因此存在编译错误。

      在调用Self::ports_names()的版本中,没有self的借用,所以是允许的。

    3. Some(self.selected_port.clone()) 在编译成功的版本中被评估为第三。这里,self.selected_portself.ports_list 是一个不同的字段,编译器知道可以安全地单独借用它们。

    我将直接方法调用移至本地可行,但我自己看不出这里有什么区别......

    一般来说,您可以在与其他借用的同时对结构的特定字段进行可变借用,但您不能拥有与任何东西重叠的可变借用else ——并且方法调用总是借用整个结构,保证重叠。

    第三个工作版本的重要区别不是self.ports_names() 存储在局部变量中,而是self.ports_names() 被评估之前 &amp;mut self.ports_list 被评估。局部变量只是为了方便实现这一点。

    例如,如果 PickList 是您的一种类型,您可以将其写为结构文字而不是调用 new() 函数,那么您可以按照不冲突的顺序编写字段初始值设定项:

    let pick_list = PickList {
        options: self.ports_names(),
        selected: Some(self.selected_port.clone()),
        on_selected: Message::PortSelected
    
        // Write this last and it doesn't conflict with any temporary immutable borrows above.
        state: &mut self.ports_list,
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-01
      • 1970-01-01
      • 2019-05-03
      • 2014-11-29
      • 2020-08-17
      • 1970-01-01
      相关资源
      最近更新 更多