Pass self reference to contained object's function - reference

I'm trying to grok Rust's ownership model. I'm trying to pass a reference to a containing object when calling a function on a struct.
Here's my struct:
pub struct Player {}
impl Player {
pub fn receive(self, app: &App) {
}
}
As you can see, receive expects a reference to an App object.
pub struct App {
pub player: Player,
}
impl App {
pub fn sender(self) {
// how to call player.test() and pass self as a reference?
self.player.receive(&self);
}
}
The above code gives me "use of partially moved value: self". Which makes sense, because App has move semantics so the value was moved into the sender function when it was called.
If I change it so that sender takes a reference to self instead, I get "cannot move out of borrowed content", which also sort of makes sense because we've borrowed the reference to self when we went into the sender function.
So what do I do? I understand why I can't store a reference to App inside Player, since that would lead to a doubly-linked structure. But I should be able to borrow a reference and perform operations on it, no?
I couldn't find an answer in the official tutorial.
I solved it by passing self as a reference in receive. But what if I want app to be mutable in receive? I can't pass self as mutable in sender because I'm also borrowing player as mutable.

One way to follow Shepmaster's solution
disassociate player from self before calling the method.
Is to put the player in an Option:
impl App {
pub fn sender(&mut self) {
let mut player = self.player.take();
player.receive(&mut self);
self.player = Some(player);
}
}
One last resource is to use RefCell.

because App has move semantics so the value was moved into the sender function when it was called.
It's true that it was moved into sender, but that's not what this message is about. Because Player::receive takes self by value, you actually had to decompose app and move player out of it to be able to call receive. At that point in time, app is now half-formed; it has no valid value for player! If receive tried to access app.player, it would be using invalid memory.
"cannot move out of borrowed content" [...] because we've borrowed the reference to self when we went into the sender function.
Right, which ties into above. Because we are borrowing an App, we cannot move player out of it, leaving the App in a invalid state.
I should be able to borrow a reference and perform operations on it, no?
And you can, so long as the thing you are taking a reference to is completely formed at that point. There were also two hints in the above exposition:
If receive tried to access app.player
If you don't access app.player in receive, restructure your code to pass the other components of App instead of the entire container. Maybe you have some GameState that is really what you want to pass.
leaving the App in a invalid state
You can use something like mem::replace to put in a different Player into app. Then it's still completely (but differently) formed and can have a reference to it taken again.
Of course, the more practical solution is to change to accept references (&self).
But what if I want app to be mutable in receive?
Yup! You'd get "cannot borrow *self as mutable more than once at a time". The solutions are actually basically the same, however! Decompose your App into smaller, non-overlapping pieces or disassociate player from self before calling the method.

Related

How can I make Rust, with the Rodio crate, load multiple sources in a Vec, so I can play them later as needed without having to load them every time?

I am using Rust with the Rodio crate and I wanted to make a Vec of loaded sources to use whenever they are needed, so that the program doesn't need to load it every time. I've made a SoundHandler class that contains an OutputStream, an OutputStreamHandle and a Vec<Decoder<BufReader<File>>>. The first two are instanced by using OutputStream::try_default(), which returns both in a tuple. The Vec is a vector of loaded sources. Its content are, as I understand it, loaded and then pushed in the following SoundHandler method:
pub fn load_source(&mut self, file_path: PathBuf) -> SoundId {
let id = self.sources.len();
self.sources.push(
Decoder::new(
BufReader::new(
File::open(file_path).expect("file not found")
)
).expect("could not load source")
);
SoundId(id)
}
Next, the source should be played by calling the play_sound() method.
Previously, it directly loaded and played the sound:
pub fn play_sound(file_path: PathBuf) {
self.stream_handle.play_raw(
Decoder::new(
BufReader::new(
File::open(file_path).expect("file not found")
)
).expect("could not load source")
);
}
But now it must handle the sources that come from the Vec. I had trouble while trying to do that. I need help with that. I am pretty new to the language so knowing how to solve that problem in more than one way would be even better, but my priority is really the path I've chosen to do it, because knowing how to solve that would undoubtedly increase my Rust problem solving skills. Keep in mind my main goal here is to learn the language, and the snippets below are not at all an urgent problem to be solved for a company.
Ok, so I tried the most straightforward thing possible. I directly changed the old method to do the same thing but with the Vec:
pub fn play_sound(&self, sound_id: SoundId) {
self.stream_handle
.play_raw(self.sources.get(sound_id.0).unwrap().convert_samples()).unwrap();
}
But the compiler won't compile that method, because, according to it,
error[E0507]: cannot move out of a shared reference
self.stream_handle.play_raw(self.sources.get(sound_id.0).unwrap().convert_samples()).unwrap();
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------------
| |
| value moved due to this method call
move occurs because value has type `rodio::Decoder<std::io::BufReader<std::fs::File>>`, which does not implement the `Copy` trait
note: this function takes ownership of the receiver `self`, which moves value
I understand what the compiler is saying. Of course that can't be possible. convert_samples() tries to make the value inside the Vec invalid, and thus it could make the Vec unsafe. So I naively tried to clone the Decoder, but the struct does not implement the Clone trait, and apparently does not have any method to do so. Finally, I found a way to get the value and own it through the Vec::remove() method, but I do not want to remove it, and I do want it to be fallible.
So my question is: in this situation, how can I make that Vec of loaded sources? Maybe I could even make a Vec out of SamplesConverter. But then the name of the type gets huge, so that is probably not an intended way to do it: Vec<SamplesConverter<Decoder<BufReader<File>>, f32>>. But it has the same problems of the other implementation.
Decoders can't be cloned because the underlying data source may not be clonable (and in fact BufReader isn't). So in order to clone the audio sources you will need to ensure that the data is stored in memory. This can be accomplished by the buffered method, which returns a Buffered source, which can be cloned. So something like this should work:
pub fn load_source(&mut self, file_path: PathBuf) -> SoundId {
let id = self.sources.len();
self.sources.push(
Decoder::new(
BufReader::new(
File::open(file_path).expect("file not found")
)
).expect("could not load source")
.buffered()
);
SoundId(id)
}
pub fn play_sound(&self, sound_id: SoundId) {
self.stream_handle.play_raw(
self.sources.get(sound_id.0).unwrap().clone().convert_samples()).unwrap();
}

Must a captured variable type implement Copy trait when closure returned as output parameter?

Question is general, no particular example.
If we have a function that returns a closure and closure itself returns some captured variable, does that variable type have to implement Copy˙trait?
From documentation:
https://doc.rust-lang.org/rust-by-example/fn/closures/output_parameters.html
fn create_fn() -> impl Fn() {
let text = "Fn".to_owned();
move || println!("This is a: {}", text)
}
this works, but for some types this solution give error. For example, if a text would be of type Option<T> it returns error saying something like:
`move occurs because `text` has type `std::option::Option<Box<dyn Something>>`, which does not implement the `Copy` trait
captured by this `Fn` closure`
One solution to solve the error is either to change closure trait to FnOnce but that introduces restriction that closure can be called only once.
But, if we want to call it multiple times, does that necessarily means that text must implement Copy trait?
I thought that move keyword does exactly that but i'm obviously wrong so if someone could explain details about how this moving captured variables actually works?
No, the type must not necessarily implement Copy. The thing is, when you have a Fn, it must be callable several times. If you actually move out something, then it's not going to work unless you're not actually moving it but just copying it (thus the error). But, the important thing is, you're not necessarily moving something out. You could also, for instance, create it on the fly (well, technically speaking, you'd still be moving it out, but maybe the example will help).
fn create_fn() -> impl Fn() -> String {
let text = "something";
move || text.to_string()
}
(see the playground)
Note that, here, String does not implement Copy (and it could also not implement Clone). What makes it work is that this function can be called over and over again, because each time it won't move out something it will need in a future call (here, by creating a brand new String to be given out).
But, if you're moving out a captured variable, you won't be able to do that twice. In that specific case, it would have to implement Copy (or just Clone and then you'd have to .clone() it).

Is unsafe my only option to pass a reference to this method expecting a closure?

I'm using the cursive_table_view crate, and the table has a set_on_submit method to which you pass a closure to be executed when the user press over a selected row in the table. The signature of said method is:
pub fn set_on_submit<F>(&mut self, cb: F)
where
F: Fn(&mut Cursive, usize, usize) + 'static,
Inside the closure, I'm trying to use something that actually comes as a parameter in the function that contains this code. I simply declare that parameter as &'static, and that's all good and expected. Now of course, I've just moved the problem to the caller of my function.
fn my_function(param: &'static MyType) {
let table = TableView::new();
table.set_on_sumbit(|cur, row, index| {
do_something_with(param);
}
}
Eventually I get to a point where I have to declare my variable as static to pass it as a parameter to that function, but because I need to modify this variable before the call (and after declaration), it needs to be static mutable. Now the problem is, when I try to modify the variable, the compiler says that I can only do that with unsafe.
I believe I understand the why of everything above. The closure is going to be called upon some event in the UI, so it makes sense that this lives for as long as the entire program.
I've tried several options with Rc and read a ton about lifetimes and even threads and messages, but I felt I was blindly trying things without really understanding at some point. Where I'm at now is that I believe I understand, but I'm uncomfortable with having to go the unsafe route suggestion.
Is it avoidable? Is my pattern wrong to begin with?
I didn't want to add more code to keep the explanation agnostic to everything else, but of course I'm happy to edit if more code helps understand the issue. Also, I don't really need to modify the struct any longer by the time I've passed the reference to the closure, in case that helps find a solution down that path.
Think of references as mostly (excluding &'static and a few other cases) being for temporary things, especially temporary things that live only as long as a stack frame. Therefore, don't try to use references for things like user interface callbacks.
Here's how to use std::rc::Rc instead, which ensures that the data lives as long as needed.
use std::rc::Rc;
fn my_function(param: Rc<MyType>) { // changed parameter type
let table = TableView::new();
table.set_on_submit(|cur, row, index| {
do_something_with(&param); // & enables conversion to &MyType
}
}
Now, in order to use this, you need to arrange so your data is owned by the Rc. It is not possible to have the above sort of callback and also have let data = MyType {...} directly, because the data must be owned by the Rc. (However, if useful, you can initialize it and then later put it in the Rc.)
fn main() {
// Wrap it as soon as you construct it.
// (Or at least before you start using callbacks.)
let my_app_data = Rc::new(MyType {
// whatever fields or constructor...
});
// Call the function. `.clone()` here makes a copy of the `Rc` refcounted pointer,
// not the underlying data.
my_function(my_app_data.clone());
}
If your program uses threads, then you must use the thread-safe reference counter std::sync::Arc instead of Rc.
If you want to modify the data while the callback exists — whether in a callback or elsewhere — you need to also use RefCell (or thread-safe std::sync::Mutex / RwLock) inside the Rc, which adds a few complications.

Method call on clap::App moving ownership more than once

Even after reading the chapters about reference ownership and borrowing, I cannot understand some things in the following code, effectively stopping me from calling more than one method from clap::App!
extern crate clap;
use clap::App;
fn main() {
let mut app =
App::new("name me").args_from_usage("<input_file> 'Sets the input file to use'");
let matches = app.get_matches();
app.print_help();
println!(
"Using input file: {}",
matches.value_of("input_file").unwrap()
);
}
Compiling this code leads to:
error[E0382]: use of moved value: `app`
--> src/main.rs:9:5
|
8 | let matches = app.get_matches();
| --- value moved here
9 | app.print_help();
| ^^^ value used here after move
|
= note: move occurs because `app` has type `clap::App<'_, '_>`, which does not implement the `Copy` trait
If I understand correctly, app.get_matches() asks to borrow the ownership, thus app must be mut. Where does the ownership go once the function returns?
I thought app would still have ownership of the object, yet the compiler has a different opinion.
How can I get the matches, and effectively still call another method, such as print_help on app then?
Read the function signature for App::get_matches:
fn get_matches(self) -> ArgMatches<'a>
This takes self by value, also said as it consumes the value; you cannot call any methods on it afterwards. There's nothing to be done about this; presumably the author has good rationale for this.
Now review App::print_help:
fn print_help(&mut self) -> ClapResult<()>
It takes a reference (which happens to be mutable). You do not have to transfer ownership to call this method.
If I understand correctly, app.get_matches() asks to borrow the ownership, thus app must be mut. Where does the ownership go once the function returns?
You do not understand correctly, in multiple dimensions.
get_matches consumes the value, it does not borrow anything.
A value does not need to be mutable to be borrowed.
When you do borrow something, the ownership doesn't "go" anywhere. The original owner continues to own it. That's why it's called borrowing.
How can I get the matches, and effectively still call another method, such as print_help on app then?
You don't. The obvious workaround is to clone the original object, producing a second value. Then you can consume one value and still call methods on the second value.
Basically, it sounds like you are trying to do something that the library is discouraging you from doing. Perhaps you should re-evaluate your goals and/or review the intended usage of the library. For example, get_matches will automatically display the help text when the user requests it, so why should your code try to do so?
From the Clap issue tracker:
You have a few options. You can either use AppSettings::ArgRequiredElseHelp or you can keep the move from happening by using App::get_matches_from_safe_borrow.

Optional function argument that is specified as a trait instead of a concrete type

I have been watching Rust for the past few months but I just started into an actual project. I am not sure if the terminology in the title is correct. Please let me know how it can be corrected.
I am writing a rust wrapper around the ENet library (http://enet.bespin.org). My goal is to keep the rust API as similar to the C API as possible except to refactor functions that take C style handle pointers into member functions of structure objects. I want to keep the API similar so that the official C documentation will apply equally well to the rust wrapper.
ENet exposes a single function to create either a client host or a server host. When creating a server you pass a pointer to an IP Address structure to the function. When creating a client you pass NULL.
I am trying to emulate that behavior using the ToSocketAddr trait and Option but I am running into problems using them in conjunction.
This is a reduced example of what I am trying to do:
use std::io::net::ip::ToSocketAddr;
fn create_host<A: ToSocketAddr>(addr: Option<A>) {
match addr {
Some(a) => println!("Address is {}. Return a server host object.",a.to_socket_addr()),
None => println!("no address... Return a client.")
};
}
fn main() {
create_host(Some("localhost:12345"));
create_host(None);
}
The first call to create_host() works like a charm. The second call however will not compile.
Rustc returns
error: unable to infer enough type information about `_`; type annotations required
I am guessing that error is occurring because None doesn't provide any resolution to the generic A. I tried the following but this doesn't work either because ToSocketAddr does not implement the trait core::kinds::Sized.
fn create_host(addr: Option<ToSocketAddr>) {
...
}
Is there a way I can do this or do I need to take a different approach?
fn main() {
create_host(Some("localhost:12345"));
create_host(None::<&str>);
}
I've chosen &str here as it is the same type as on the first call, so that the compiler wouldn't generate another monomorphized version of the generic function. You could choose any type that implements ToSocketAddr.

Resources