Reputation: 21
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
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:
&mut self.ports_list
is evaluated first and stored as a temporary value. Now, self.ports_list
is mutably borrowed.
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.
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