I am trying to create a requestAnimationFrame loop, which will call game.render() for every frame. I am following this tutorial - https://rustwasm.github.io/wasm-bindgen/examples/request-animation-frame.html
struct Game {
state: GameState,
ctx: web_sys::CanvasRenderingContext2d
}
impl Game {
fn render(self: Game) {
self.ctx.begin_path();
self.ctx.arc(self.state.x, 50.0, 40.0, 0.0, 2.0 * std::f64::consts::PI);
self.ctx.stroke();
}
}
#[wasm_bindgen(start)]
pub fn run() -> Result<(), JsValue> {
let game = init();
let f = Rc::new(RefCell::new(None));
let g = f.clone();
*g.borrow_mut() = Some(Closure::wrap(Box::new(move || {
game.render();
request_animation_frame(f.borrow().as_ref().unwrap());
}) as Box<dyn FnMut()>));
request_animation_frame(g.borrow().as_ref().unwrap());
Ok(())
}
fn init() -> Game {
let doc = document();
let canvas = doc.create_element("canvas").unwrap();
canvas.set_attribute("width", "800px").unwrap();
canvas.set_attribute("height", "800px").unwrap();
canvas.set_id("fp-canvas");
body().append_child(&canvas).expect("Could not attach canvas");
Game {
ctx: context(),
state: GameState {
x: 3.0
}
}
}
But it gives following error -
error[E0277]: expected a `std::ops::FnMut<()>` closure, found `[closure#src/lib.rs:89:51: 92:6 game:Game, f:std::rc::Rc<std::cell::RefCell<std::option::Option<wasm_bindgen::closure::Closure<dyn std::ops::FnMut()>>>>]`
--> src/lib.rs:89:42
|
89 | *g.borrow_mut() = Some(Closure::wrap(Box::new(move || {
| __________________________________________^
90 | | game.render();
91 | | request_animation_frame(f.borrow().as_ref().unwrap());
92 | | }) as Box<dyn FnMut()>));
| |______^ expected an `FnMut<()>` closure, found `[closure#src/lib.rs:89:51: 92:6 game:Game, f:std::rc::Rc<std::cell::RefCell<std::option::Option<wasm_bindgen::closure::Closure<dyn std::ops::FnMut()>>>>]`
|
= help: the trait `std::ops::FnMut<()>` is not implemented for `[closure#src/lib.rs:89:51: 92:6 game:Game, f:std::rc::Rc<std::cell::RefCell<std::option::Option<wasm_bindgen::closure::Closure<dyn std::ops::FnMut()>>>>]`
= note: wrap the `[closure#src/lib.rs:89:51: 92:6 game:Game, f:std::rc::Rc<std::cell::RefCell<std::option::Option<wasm_bindgen::closure::Closure<dyn std::ops::FnMut()>>>>]` in a closure with no arguments: `|| { /* code */ }
= note: required for the cast to the object type `dyn std::ops::FnMut()`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
error: could not compile `fighting-pixel`.
To learn more, run the command again with --verbose.
If I comment out game.render() it compiles. But I want to keep a state which will be updated to create an animation. What I am doing wrong? Why Closure doesn't allow to call struct methods?
Thanks in advance for your help.
I found the issue. The Game struct would be like -
impl Game {
fn render(self: &Game) {
self.ctx.begin_path();
self.ctx.arc(self.state.x, 50.0, 40.0, 0.0, 2.0 * std::f64::consts::PI);
self.ctx.stroke();
}
}
Forgot to put & symbol for self. Thanks Pauan#6666 from Discord channel #wg-wasm for pointing that out.
Shimul comment worked for me.
I share here my game start_loop blocking function.
where the insteresting line is at the end:
draw_scene(&self.state, &self.gl_context).unwrap();
impl GameGl { pub fn start_loop(mut self) -> Result<(), JsValue> {
// Render loop
// Dont ask me
let f = Rc::new(RefCell::new(None));
let g = f.clone();
const FPS_THROTTLE: f64 = 1000.0 / 60.0; // milliseconds / frames
let mut previous: f64 = js_sys::Date::now();
*g.borrow_mut() = Some(Closure::wrap(Box::new(move || {
request_animation_frame(f.borrow().as_ref().unwrap());
// Get time (miliseconds)
let now = js_sys::Date::now();
// Clause: must work or sleeep ?
// The current rotation angle 1 rad/sec
if now < previous + FPS_THROTTLE {
return ();
}
// Update time
let delta_time = now - previous;
previous = now;
// Update game
self.state.cube_rotation += delta_time as f32 * 0.001;
// Draw
draw_scene(&self.state, &self.gl_context).unwrap();
//&self.gl, &self.program_info, &self.texture, &buffers, &state).unwrap();
}) as Box<dyn FnMut()>));
console::log_1(&"Requesting animation frame".into());
request_animation_frame(g.borrow().as_ref().unwrap());
//let program_info =
Ok(())
}}
Related
A few months ago, I started learning Rust and Bevy. After a few projects my code started being repetitive, and too large to be concise and stay in a single function. To solve this problem I wrote a small macro to generate type aliases for queries and it's items and make passing their references easier.
macro_rules! mut_sys_alias {
(
$type_name:ident, $wq_q:tt, $wq_f:tt
) => {
paste::paste!{
type $type_name <'w, 's> = Query<'w, 's, $wq_q, $wq_f>;
type [<$type_name Item >]<'w> = QueryItem<'w, $wq_q>;
}
};
}
It is used like so:
mut_sys_alias! {
QContainables,
(
&'static mut Transform,
&'static GlobalTransform,
),
(
With<Containable>,
With<Draggable>,
Without<Contained>,
)
}
mut_sys_alias! {
QContained,
(
&'static mut Transform,
&'static GlobalTransform,
&'static mut Contained,
&'static Parent,
),
(
With<Containable>,
With<Draggable>,
)
}
mut_sys_alias! {
QContainers,
(
Entity,
&'static mut Container,
&'static GlobalTransform,
&'static Children,
),
()
}
This is where the problem occures:
pub fn us_container(
mut commands: Commands,
removed_dragging: RemovedComponents<Dragging>,
mut q_containables: QContainables,
mut q_contained: QContained,
mut q_containers: QContainers,
) {
for removed_dragging_entity in removed_dragging.iter() {
// Check if the current dagging entity was in a container previously
if let Ok(mut t_containable) = q_containables.get_mut(removed_dragging_entity) {
// NO it wasn't
// Loop trough all potential containers and see which is the most suitable
for mut t_container in q_containers.iter_mut() {
// Add to container
if util_add_to_container(
&mut commands,
removed_dragging_entity,
&mut t_containable,
&mut t_container,
&mut q_contained,
)
.is_ok()
{
return;
}
}
} else if let Ok(mut t_contained) = q_contained.get_mut(removed_dragging_entity) {
util_remove_from_container(
&mut commands,
removed_dragging_entity,
&mut t_contained,
&mut q_contained,
&mut q_containers,
);
// Previous code
// let (mut contained_tran, contained_gtran, contained_compnent, contained_parent) =
// t_contained;
// if let Ok(t_container) = q_containers.get_mut(contained_parent.get()) {
// let (container_entity, mut container, _container_gtran, container_children) =
// t_container;
// commands
// .entity(removed_dragging_entity)
// .remove::<Contained>();
// contained_tran.translation = contained_gtran.translation();
// commands
// .entity(container_entity)
// .remove_children(&[removed_dragging_entity]);
// container.len -= 1;
// let idx = contained_compnent.idx;
// for child_entity in container_children.iter() {
// if let Ok(t_contained) = q_contained.get_mut(*child_entity) {
// let (_contained_tran, _contained_gtran, mut contained_compnent, _) =
// t_contained;
// if contained_compnent.idx > idx as u8 {
// contained_compnent.idx -= 1;
// }
// }
// }
// }
}
}
}
fn util_remove_from_container(
commands: &mut Commands,
removed_dragging_entity: Entity,
t_contained: &mut QContainedItem,
q_contained: &mut QContained,
q_containers: &mut QContainers,
) {
let (contained_tran, contained_gtran, contained_compnent, contained_parent) = t_contained;
if let Ok(t_container) = q_containers.get_mut(contained_parent.get()) {
let (container_entity, mut container, _container_gtran, container_children) = t_container;
commands
.entity(removed_dragging_entity)
.remove::<Contained>();
contained_tran.translation = contained_gtran.translation();
commands
.entity(container_entity)
.remove_children(&[removed_dragging_entity]);
container.len -= 1;
let idx = contained_compnent.idx;
for child_entity in container_children.iter() {
if let Ok(t_contained) = q_contained.get_mut(*child_entity) {
let (_contained_tran, _contained_gtran, mut contained_compnent, _) = t_contained;
if contained_compnent.idx > idx as u8 {
contained_compnent.idx -= 1;
}
}
}
}
}
I tried to move the commented code to a separate function and needed to pass &mut t_contained and &mut q_contained but the compiler complained:
error[E0499]: cannot borrow `q_contained` as mutable more than once at a time
--> src\setup\container.rs:177:17
|
172 | } else if let Ok(mut t_contained) = q_contained.get_mut(removed_dragging_entity) {
| -------------------------------------------- first mutable borrow occurs here
173 | util_remove_from_container(
| -------------------------- first borrow later used by call
...
177 | &mut q_contained,
| ^^^^^^^^^^^^^^^^ second mutable borrow occurs here
For more information about this error, try `rustc --explain E0499`
I don't understand why the previous version of the code(the one without the function) worked, while the new one does not. I know that get_mut takes &mut self as an argument but still, I don't see why q_contained.get_mut(removed_dragging_entity) counts as a borrow inside the if let statement.
Can this be fixed somehow, and are there better ways to write modular code and not pass every component separately or in a tuple?
I'm trying to learn Rust after many years of C++. I have a situation where the compiler is complaining about a borrow, and it doesn't seem to matter whether it is mutable or immutable. I don't seem to be able to use self as a parameter inside a loop that start with: for item in self.func.drain(..).I've tried calling appropriate() as a function:
Self::appropriate(&self,&item,index)
and I have tried it as a method:
self.appropriate(&item,index)
but I get the same message in either case:
The function or method appropriate() is intended imply examine the relationship among its parameters and return a bool without modifying anything. How can I call either a function or method on self without violating borrowing rules?This program is a learning exercise from exercism.org and doesn't include a main() so it won't run but should almost compile except for the error in question. Here's the code I have:
use std::collections::HashMap;
pub type Value = i32;
pub type Result = std::result::Result<(), Error>;
pub struct Forth {
v: Vec<Value>,
f: HashMap<String,usize>,
s: Vec<Vec<String>>,
func: Vec<String>
}
#[derive(Debug, PartialEq)]
pub enum Error {
DivisionByZero,
StackUnderflow,
UnknownWord,
InvalidWord,
}
impl Forth {
pub fn new() -> Forth {
let mut temp: Vec<Vec<String>> = Vec::new();
temp.push(Vec::new());
Forth{v: Vec::<Value>::new(), f: HashMap::new(), s: temp, func: Vec::new()}
}
pub fn stack(&self) -> &[Value] {
&self.v
}
pub fn eval(&mut self, input: &str) -> Result {
self.v.clear();
self.s[0].clear();
let mut count = 0;
{
let temp: Vec<&str> = input.split(' ').collect();
let n = temp.len() as i32;
for x in 0..n as usize {
self.s[0].push(String::from(temp[x]));
}
}
let mut collecting = false;
let mut xlist: Vec<(usize,usize)> = Vec::new();
let mut sx: usize = 0;
let mut z: i32 = -1;
let mut x: usize;
let mut n: usize = self.s[0].len();
loop {
count += 1;
if count > 20 {break;}
z += 1;
x = z as usize;
if x >= n {break;}
z = x as i32;
let word = &self.s[sx][x];
if word == ";" {
if collecting {
collecting = false;
let index: usize = self.s.len();
self.s.push(Vec::<String>::new());
for item in self.func.drain(..) {
if self.s[index].len() > 0 &&
Self::appropriate(&self,&item,index)
{
let sx = *self.f.get(&self.s[index][0]).unwrap();
let n = self.s[sx].len();
for x in 1..n as usize {
let symbol = self.s[sx][x].clone();
self.s[index].push(symbol);
}
}
else {
self.s[index].push(item);
}
}
self.f.insert(self.s[index][0].clone(), index);
self.func.clear();
continue;
}
if 0 < xlist.len() {
(x, n) = xlist.pop().unwrap();
continue;
}
return Err(Error::InvalidWord);
}
if collecting {
self.func.push(String::from(word));
continue;
}
if Self::is_op(word) {
if self.v.len() < 2 {
return Err(Error::StackUnderflow);
}
let b = self.v.pop().unwrap();
let a = self.v.pop().unwrap();
let c = match word.as_str() {
"+" => a + b,
"-" => a - b,
"*" => a * b,
"/" => {if b == 0 {return Err(Error::DivisionByZero);} a / b},
_ => 0
};
self.v.push(c);
continue;
}
match word.parse::<Value>() {
Ok(value) => { self.v.push(value); continue;},
_ => {}
}
if word == ":" {
collecting = true;
self.func.clear();
continue;
}
if word == "drop" {
if self.v.len() < 1 {
return Err(Error::StackUnderflow);
}
self.v.pop();
continue;
}
if word == "dup" {
if self.v.len() < 1 {
return Err(Error::StackUnderflow);
}
let temp = self.v[self.v.len() - 1];
self.v.push(temp);
continue;
}
if !self.f.contains_key(word) {
return Err(Error::UnknownWord);
}
xlist.push((sx,n));
sx = *self.f.get(word).unwrap();
n = self.s[sx].len();
z = 0;
}
Ok(())
}
fn is_op(input: &str) -> bool {
match input {"+"|"-"|"*"|"/" => true, _ => false}
}
fn appropriate(&self, item:&str, index:usize) -> bool
{
false
}
fn prev_def_is_short(&self, index: usize) -> bool {
if index >= self.s.len() {
false
}
else {
if let Some(&sx) = self.f.get(&self.func[0]) {
self.s[sx].len() == 2
}
else {
false
}
}
}
}
The error message relates to the call to appropriate(). I haven't even written the body of that function yet; I'd like to get the parameters right first. The compiler's complaint is:
As a subroutine call
error[E0502]: cannot borrow `self` as immutable because it is also borrowed as mutable
--> src/lib.rs:85:47
|
81 | for item in self.func.drain(..) {
| -------------------
| |
| mutable borrow occurs here
| mutable borrow later used here
...
85 | Self::appropriate(&self,&item,index)
| ^^^^^ immutable borrow occurs here
For more information about this error, try `rustc --explain E0502`.
as a method call
error[E0502]: cannot borrow `*self` as immutable because it is also borrowed as mutable
--> src/lib.rs:85:29
|
81 | for item in self.func.drain(..) {
| -------------------
| |
| mutable borrow occurs here
| mutable borrow later used here
...
85 | self.appropriate(&item,index)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow occurs here
For more information about this error, try `rustc --explain E0502`.
Is there any canonical way to deal with this situation?
The problem is that self.func.drain() will consume the elements contained in self.func, thus an exclusive (&mut) access is needed on self.func for the entire for loop.
If during the iteration you need to pass a reference to self globally, then its func member is potentially accessible while the loop holds an exclusive access to it: Rust forbids that.
Since you use drain() in order to consume all the elements inside self.func, I suggest you swap this vector with an empty one just before the loop, then iterate on this other vector that is not anymore part of self.
No copy of the content of the vector is involved here; swap() only deals with pointers.
Here is an over-simplified version of your code, adapted consequently.
struct Forth {
func: Vec<String>,
}
impl Forth {
fn eval(&mut self) {
/*
for item in self.func.drain(..) {
self.appropriate(&self);
}
*/
let mut func = Vec::new();
std::mem::swap(&mut self.func, &mut func);
for item in func.drain(..) {
let b = self.appropriate();
println!("{:?} {:?}", item, b);
}
}
fn appropriate(&self) -> bool {
false
}
}
fn main() {
let mut f = Forth {
func: vec!["aaa".into(), "bbb".into()],
};
f.eval();
}
I am working on the Rustlings course Errors3.rs:
// This is a program that is trying to use a completed version of the
// `total_cost` function from the previous exercise. It's not working though!
// Why not? What should we do to fix it?
use std::num::ParseIntError;
fn main() {
let mut tokens = 100;
let pretend_user_input = "8";
let cost = total_cost(pretend_user_input)?;
if cost > tokens {
println!("You can't afford that many!");
} else {
tokens -= cost;
println!("You now have {} tokens.", tokens);
}
}
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;
let qty = item_quantity.parse::<i32>()?;
Ok(qty * cost_per_item + processing_fee)
}
Here is my current code:
use std::num::ParseIntError;
fn main() -> Result<String, String> {
let mut tokens = 100;
let pretend_user_input = "8";
let cost = total_cost(pretend_user_input)?;
if cost > tokens {
//println!("You can't afford that many!");
Ok(format!("You can't afford that many!"))
} else {
tokens -= cost;
//println!("You now have {} tokens.", tokens);
Err(format!("You now have {} tokens.", tokens).to_string())
}
}
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;
let qty = item_quantity.parse::<i32>()?;
Ok(qty * cost_per_item + processing_fee)
}
I am at a loss as to how to correct the first error:
error[E0277]: `?` couldn't convert the error to `std::string::String`
--> src/main.rs:7:46
|
7 | let cost = total_cost(pretend_user_input)?;
| ^ the trait `std::convert::From<std::num::ParseIntError>` is not implemented for `std::string::String`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following implementations were found:
<std::string::String as std::convert::From<&std::string::String>>
<std::string::String as std::convert::From<&str>>
<std::string::String as std::convert::From<std::borrow::Cow<'a, str>>>
<std::string::String as std::convert::From<std::boxed::Box<str>>>
= note: required by `std::convert::From::from`
error[E0277]: `main` has invalid return type `std::result::Result<std::string::String, std::string::String>`
--> src/main.rs:3:14
|
3 | fn main() -> Result<String, String> {
| ^^^^^^^^^^^^^^^^^^^^^^ `main` can only return types that implement `std::process::Termination`
|
= help: consider using `()`, or a `Result`
Based on the suggestions, regarding From, I tried changing Ok(format!("You can't afford that many!")) to Ok(String::from("You can't afford that many!")). But it results in virtually the same error message.
I have tried looking at the Rust documentation for std::convert::From. This gave me the idea of trying:
let slug: &'static str = "You can't afford that many!";
if cost > tokens {
//println!("You can't afford that many!");
Ok(std::convert::From(slug))
} else {
tokens -= cost;
//println!("You now have {} tokens.", tokens);
Err(format!("You now have {} tokens.", tokens).to_string())
}
Which results in the error:
error[E0423]: expected function, tuple struct or tuple variant, found trait `std::convert::From`
--> src/main.rs:12:12
|
12 | Ok(std::convert::From(slug))
| ^^^^^^^^^^^^^^^^^^ not a function, tuple struct or tuple variant
You tried to change the behaviour of the original program, the program must print something not return a String from the main (actually you can't return a String from the main, you must return something that implement Termination).
The solution is close to what you did, main() must also return an error, there is two way, use real type or use dynamic trait. Your case is very simple so real type is the most simple:
use std::num::ParseIntError;
fn main() -> Result<(), ParseIntError> {
let mut tokens = 100;
let pretend_user_input = "8";
let cost = total_cost(pretend_user_input)?;
if cost > tokens {
println!("You can't afford that many!");
} else {
tokens -= cost;
println!("You now have {} tokens.", tokens);
}
// we just return ok with nothing in it, this mean program terminated without error
Ok(())
}
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;
let qty = item_quantity.parse::<i32>()?;
Ok(qty * cost_per_item + processing_fee)
}
But you could also use a dynamic trait, it's more advance but not particular better:
use std::num::ParseIntError;
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let mut tokens = 100;
let pretend_user_input = "8";
let cost = total_cost(pretend_user_input)?;
if cost > tokens {
println!("You can't afford that many!");
} else {
tokens -= cost;
println!("You now have {} tokens.", tokens);
}
Ok(())
}
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;
let qty = item_quantity.parse::<i32>()?;
Ok(qty * cost_per_item + processing_fee)
}
There is a lot of way to handle error in Rust. You can learn a lot on this blog post.
The hints say to use a different method which avoids the "?" operator.
This is my solution.
// errors3.rs
// This is a program that is trying to use a completed version of the
// `total_cost` function from the previous exercise. It's not working though!
// Why not? What should we do to fix it?
// Execute `rustlings hint errors3` for hints!
// I AM NOT DONE
use std::num::ParseIntError;
fn main() -> Result<(),ParseIntError>{
let mut tokens = 100;
let pretend_user_input = "8";
let cost = total_cost(pretend_user_input);
match cost {
Ok(x) if x > tokens =>
println!("You can't afford that many!"),
Ok(x) => {
tokens -= x;
println!("You now have {} tokens.", tokens);
},
Err(e) => (),
}
Ok(())
}
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;
let qty = item_quantity.parse::<i32>()?;
Ok(qty * cost_per_item + processing_fee)
}
I'm experimenting with futures 0.3 for the first time, and getting trait or lifetime issues.
use futures::executor::ThreadPool;
use futures::future::*;
use futures::StreamExt;
const BUFSIZE: usize = 140;
#[derive(Clone)]
struct Packet {
channel: usize,
seq: usize,
total: usize,
buffer: [u8; BUFSIZE],
}
fn to_packets<T>(channel: usize, msg: T) -> Vec<Packet> {
// Trivial implemmentation
vec![Packet {
channel: 0,
seq: 0,
total: 0,
buffer: [0u8; BUFSIZE],
}]
}
pub struct SMConnection;
impl SMConnection {
fn push<T: 'static>(&mut self, channel: &'static usize, msg: T)
where
T: Send + Sync + Clone + Serialize,
{
let threadpool = ThreadPool::new().unwrap();
let future = async {
move || {
to_packets(*channel, msg)
.iter()
.for_each(|_packet| { /* do something */ })
};
};
threadpool.spawn_ok(future);
}
}
This errors with
error[E0597]: `channel` does not live long enough
--> src\network.rs:82:27
|
81 | let future = async {
| ______________________-_____-
| |______________________|
| ||
82 | || let channel = channel.clone();
| || ^^^^^^^ borrowed value does not live long enough
83 | || move || to_packets(channel, msg).iter().for_each(|_| { });
84 | || };
| || -
| ||_________|
| |__________value captured here by generator
| argument requires that `channel` is borrowed for `'static`
85 | threadpool.spawn_ok(future);
86 | }
| - `channel` dropped here while still borrowed
I'm passing channel and msg by value so I would not expect there to be a lifetime issue. Following the compiler advice to give 'static bounds to the arguments still leaves me stuck.
I've tried combinations of clone and Arc
What have I missed here?
EDIT For reference: Cargo.toml
[dependencies]
serde="^1"
serde_derive="^1"
bincode = "1.1.4"
romio = "0.3.0-alpha.9"
futures-preview = { version = "=0.3.0-alpha.18", features = ["async-await", "nightly"] }
Compiler: rustc 1.39.0-nightly (9b91b9c10 2019-08-26)
After playing around with it and specifically reading How can I send non-static data to a thread in Rust and is it needed in this example?, I realised that you can keep the interface the same and shield it from users if you 1) move the value into the async future and 2) wrap the values in an Arc and using the values from behind the reference counted wrapper
fn push<T: 'static>(&mut self, channel: usize, msg: T)
where
T: Send + Sync + Clone + Serialize,
{
let threadpool = ThreadPool::new().unwrap();
let future = async move {
let msg = Arc::from(msg);
let channel = Arc::from(channel);
move || to_packets(*channel, msg).iter().for_each(|_| {});
};
threadpool.spawn_ok(future);
}
I am just starting to learn Rust. For this purpose I am rewriting my C++ project in Rust, but the biggest problems are lifetimes of closures and such.
I created a absolute minimal scenario of my problem seen here and below:
use std::sync::Arc;
use std::cell::{RefCell, Cell};
struct Context {
handler: RefCell<Option<Arc<Handler>>>,
}
impl Context {
pub fn new() -> Arc<Context> {
let context = Arc::new(Context{
handler: RefCell::new(None),
});
let handler = Handler::new(context.clone());
(*context.handler.borrow_mut()) = Some(handler);
context
}
pub fn get_handler(&self) -> Arc<Handler> {
self.handler.borrow().as_ref().unwrap().clone()
}
}
struct Handler {
context: Arc<Context>,
clickables: RefCell<Vec<Arc<Clickable>>>,
}
impl Handler {
pub fn new(context: Arc<Context>) -> Arc<Handler> {
Arc::new(Handler{
context: context,
clickables: RefCell::new(Vec::new()),
})
}
pub fn add_clickable(&self, clickable: Arc<Clickable>) {
self.clickables.borrow_mut().push(clickable);
}
pub fn remove_clickable(&self, clickable: Arc<Clickable>) {
// remove stuff ...
}
}
struct Clickable {
context: Arc<Context>,
callback: RefCell<Option<Box<Fn()>>>,
}
impl Clickable {
pub fn new(context: Arc<Context>) -> Arc<Clickable> {
let clickable = Arc::new(Clickable{
context: context.clone(),
callback: RefCell::new(None),
});
context.get_handler().add_clickable(clickable.clone());
clickable
}
pub fn remove(clickable: Arc<Clickable>) {
clickable.context.get_handler().remove_clickable(clickable);
}
pub fn set_callback(&self, callback: Option<Box<Fn()>>) {
(*self.callback.borrow_mut()) = callback;
}
pub fn click(&self) {
match *self.callback.borrow() {
Some(ref callback) => (callback)(),
None => (),
}
}
}
struct Button {
context: Arc<Context>,
clickable: Arc<Clickable>,
}
impl Button {
pub fn new(context: Arc<Context>) -> Arc<Button> {
let clickable = Clickable::new(context.clone());
let button = Arc::new(Button{
context: context,
clickable: clickable.clone(),
});
let tmp_callback = Box::new(|| {
button.do_stuff();
});
clickable.set_callback(Some(tmp_callback));
button
}
pub fn do_stuff(&self) {
// doing crazy stuff
let mut i = 0;
for j in 0..100 {
i = j*i;
}
}
pub fn click(&self) {
self.clickable.click();
}
}
impl Drop for Button {
fn drop(&mut self) {
Clickable::remove(self.clickable.clone());
}
}
fn main() {
let context = Context::new();
let button = Button::new(context.clone());
button.click();
}
I just don't know how to pass references in closures.
Another ugly thing is that my Handler and my Context need each other. Is there a nicer way to to create this dependency?
Going off your initial code
pub fn new(context: Arc<Context>) -> Arc<Button> {
let clickable = Clickable::new(context.clone());
let button = Arc::new(Button{
context: context,
clickable: clickable.clone(),
});
let tmp_callback = Box::new(|| {
button.do_stuff();
});
clickable.set_callback(Some(tmp_callback));
button
}
First off, let's note the error you're getting
error[E0373]: closure may outlive the current function, but it borrows `button`, which is owned by the current function
--> src/main.rs:101:37
|
101 | let tmp_callback = Box::new(|| {
| ^^ may outlive borrowed value `button`
102 | button.do_stuff();
| ------ `button` is borrowed here
|
help: to force the closure to take ownership of `button` (and any other referenced variables), use the `move` keyword, as shown:
| let tmp_callback = Box::new(move || {
Noting the help block at the bottom, you need to use a move closure, because when the new function ends, the button variable on the stack will go out of scope. The only way to avoid that is to move ownership of it to the callback itself. Thus you'd change
let tmp_callback = Box::new(|| {
to
let tmp_callback = Box::new(move || {
Now, you'd get a second error:
error[E0382]: use of moved value: `button`
--> src/main.rs:107:9
|
102 | let tmp_callback = Box::new(move || {
| ------- value moved (into closure) here
...
107 | button
| ^^^^^^ value used here after move
|
= note: move occurs because `button` has type `std::sync::Arc<Button>`, which does not implement the `Copy` trait
And the error here may be a little clearer. You're trying to move ownership of the button value into the callback closure, but you also use it inside the body of the new function when you return it, and you can't have two different things trying to own the value.
The solution to that is hopefully what you'd guess. You have to make a copy that you can take ownership of. You'll want to then change
let tmp_callback = Box::new(move || {
button.do_stuff();
to
let button_clone = button.clone();
let tmp_callback = Box::new(move || {
button_clone.do_stuff();
Now you've created a new Button object, and returned an Arc for the object itself, while also giving ownership of a second Arc to the callback itself.
Update
Given your comment, there is indeed an issue here of cyclic dependencies, since your Clickable object holds ownership of a reference to Button, while Button holds ownership of a reference to Clickable. The easiest way to fix this here would be to update that code a third time, from
let button_clone = button.clone();
let tmp_callback = Box::new(move || {
button_clone.do_stuff();
to
let button_weak = Arc::downgrade(&button);
let tmp_callback = Box::new(move || {
if let Some(button) = button_weak.upgrade() {
button.do_stuff();
}
});
so the Clickable will only hold a weak reference to the Button, and if the Button is no longer referenced, the callback will be a no-op.
You'd also probably want to consider making clickables a list of Weak references instead of strong references, so you can remove items from it when the item they reference is removed.