rust - How do I upgrade a reference to a mutable reference? -


i have program revolves around 1 shared data structure, proposing changes data, , applying these changes @ later stage. these proposed changes hold references core object.

in c++ or language, make reference non-const, mutate when need to. rust doesn't play approach. (i asked in irc earlier today, sadly i'm still stuck.)

to help, made distilled example booking tickets in theatre, theatre data structure, bookings proposed changes, , run method applying them if figure out how work!

firstly, defining data structures. theatre has many rows, have many seats each:

use std::sync::{arc, rwlock}; use std::thread;  struct theatre { rows: vec<row> } struct row { seats: vec<seat> }  struct seat {     number: i32,     booked: bool, }  impl seat {     fn new(number: i32) -> seat {         seat { number: number, booked: false }     }      fn book(&mut self) {         self.booked = true;     } } 

here, get_booking method searches seat, returning booking reference seat finds.

impl theatre {     fn get_booking<'t>(&'t self, number: i32) -> option<booking<'t>> {         row in self.rows.iter() {             seat in row.seats.iter() {                 if seat.number == number && seat.booked == false {                     return some(booking { seats: vec![ seat ] })                 }             }         }          none     } } 

but stuck. run method has mutable access overall theatre (from parameter), , knows seat mutate (self). since self isn't mutable, though theatre contains is, can't mutated.

struct booking<'t> {     seats: vec<&'t seat> }  impl<'t> booking<'t> {     fn describe(&self) {         let seats: vec<_> = self.seats.iter().map(|s| s.number).collect();         println!("you want book seats: {:?}", seats);     }      fn run(&self, _theatre: &mut theatre) {         let mut seat = ??????;         seat.book();     } } 

finally, main method use if worked.

fn main() {     // build theatre (with 1 seat... small theatre)     let theatre = theatre { rows: vec![ row { seats: vec![ seat::new(7) ] } ] };     let wrapper = arc::new(rwlock::new(theatre));      // try book seat in thread     let thread = thread::spawn(move || {         let desired_seat_number = 7;          let t = wrapper.read().unwrap();         let booking = t.get_booking(desired_seat_number).expect("no such seat!");          booking.describe();          let mut tt = wrapper.write().unwrap();         booking.run(&mut tt);  // never reached because still have read lock     });      thread.join().unwrap(); } 

what's annoying know why current code doesn't work - can't figure out how rust wants program formatted instead. there things don't want do:

  • the simplest solution have booking hold index seat, instead of reference: in case, row , seat usize fields. however, although theatre uses o(1) vectors, i'd reference value in middle of large tree, having iterate find value more expensive. mean couldn't, say, seat number (in describe function) without having pass in entire theatre.
  • it solved having booking hold mutable reference seat, mutate normal. however, mean have 1 proposed change @ time: couldn't, example, have list of bookings , apply them @ once, or have 2 bookings , apply one.

i feel i'm close having rust accept, don't quite know how structure program accommodate it. so, pointers? (pun intended)

first, here's code:

use std::sync::{arc, rwlock}; use std::thread; use std::sync::atomic::{atomicbool, ordering};  struct theatre { rows: vec<row> } struct row { seats: vec<seat> }  struct seat {     number: i32,     booked: atomicbool, }  impl seat {     fn new(number: i32) -> seat {         seat { number: number, booked: atomicbool::new(false) }     }      fn book(&self) {         self.booked.store(true, ordering::release);         println!("booked seat: {:?}", self.number);     } }  impl theatre {     fn get_booking<'t>(&'t self, number: i32) -> option<booking<'t>> {         row in self.rows.iter() {             seat in row.seats.iter() {                 if seat.number == number && seat.booked.load(ordering::acquire) == false {                     return some(booking { seats: vec![ seat ] })                 }             }         }          none     } }  struct booking<'t> {     seats: vec<&'t seat> }  impl<'t> booking<'t> {     fn describe(&self) {         let seats: vec<_> = self.seats.iter().map(|s| s.number).collect();         println!("you want book seats: {:?}", seats);     }      fn run(&self) {         seat in self.seats.iter() {             seat.book();         }     } }  fn main() {     // build theatre (with 1 seat... small theatre)     let theatre = theatre { rows: vec![ row { seats: vec![ seat::new(7) ] } ] };     let wrapper = arc::new(rwlock::new(theatre));      // try book seat in thread     let thread = thread::spawn(move || {         let desired_seat_number = 7;          let t = wrapper.read().unwrap();         let booking = t.get_booking(desired_seat_number).expect("no such seat!");          booking.describe();          booking.run();     });      thread.join().unwrap(); } 

view on playpen

there 2 important changes:

  1. the booked field changed bool atomicbool. atomic types provide store method available on immutable references. therefore, can make seat::book() take self immutable reference. if have more complex type not covered atomic types, should instead use mutex or rwlock.
  2. i removed &mut theatre parameter on booking::run(). if not acceptable, please leave comment explain why need reference.

as found, cannot have both read lock , write lock active @ same time on rwlock. however, booking cannot live longer read lock on theatre, because contains references inside theatre. once release read lock, cannot guarantee references obtained remain valid when acquired lock later on. if that's problem, consider using arc instead of simple borrowed pointers (&).


Comments

Popular posts from this blog

OpenCV OpenCL: Convert Mat to Bitmap in JNI Layer for Android -

android - org.xmlpull.v1.XmlPullParserException: expected: START_TAG {http://schemas.xmlsoap.org/soap/envelope/}Envelope -

python - How to remove the Xframe Options header in django? -