Levitanus
Levitanus

Reputation: 21

Cannot borrow self twice in one function call

I'm a newbie in rust, and stunned with making function call. I'm sorry for I cannot make clean error example, as error occurs in the constructor of iced::PickList.

The matter is — as long as I use only one method in that call — the build pass and programm works, but if I just change the signature of the second needed method for be not static — I can not build and fix bowwor errors at all.

The complete working (builds and launches) example on the playground.

While it looks like this, with Self::port_names() call, it works.

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()
    }
}

If I just replace the method signature — problems begin from «immutable borrow can not be performed along with mutable» until «temporary resource cannot be returned from function».

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()
    }
}

Please, help me understand why is it happens and how to avoid such problems.

[EDIT]

I feel, I still don't get the fundamental concept of the variable\local variable in rust, as when i changed my view function to this:

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()
    }

It worked. I understand that the reason is — I moved direct method call to the local viable, but I can see no difference here for myself...

Upvotes: 0

Views: 654

Answers (1)

Kevin Reid
Kevin Reid

Reputation: 43773

Whenever you have a compound expression like a structure constructor or a function call,

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

evaluation of it proceeds in a very specific fashion: first, each of the argument expressions is evaluated, in the order they're written in the source code, and their values are stored temporarily on the stack. Then, once all the argument values are ready, the function is called.

In this case:

  1. &mut self.ports_list is evaluated first and stored as a temporary value. Now, self.ports_list is mutably borrowed.

  2. self.ports_names() is evaluated second — but self.ports_list is mutably borrowed, and calling ports_names requires an immutable borrow of all of self, which is not allowed so there is a compile error.

    In the version that calls Self::ports_names(), there is no borrow of self, so this is permitted.

  3. Some(self.selected_port.clone()) is evaluated third, in the versions that compile successfully. Here, self.selected_port is a different field than self.ports_list, and the compiler understands that they may be safely borrowed separately.

I moved direct method call to the local viable, but I can see no difference here for myself...

In general, you can have mutable borrows of specific fields of a structure at the same time as other borrows, but you cannot have a mutable borrow that overlaps with anything else — and a method call always borrows the entire structure, guaranteeing overlap.

The important difference in your third, working version is not that self.ports_names() is stored in a local variable, but that self.ports_names() is evaluated before &mut self.ports_list is evaluated. The local variable is just a convenience to make that happen.

For example, if PickList were a type of yours that you could write as a structure literal instead of calling a new() function, then you could write the field initializers in an order that doesn't conflict:

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,
}

Upvotes: 2

Related Questions