Skip to content

Instantly share code, notes, and snippets.

@aboglioli
Last active May 3, 2020 23:24
Show Gist options
  • Save aboglioli/4c0dce8ce7e27126a5b8527cea568a99 to your computer and use it in GitHub Desktop.
Save aboglioli/4c0dce8ce7e27126a5b8527cea568a99 to your computer and use it in GitHub Desktop.
Example of self-referenced struct. World struct has multiple players and a property called 'current' referencing one of the players in vector.
use std::cell::RefCell;
use std::rc::Rc;
// Using Rc and RefCell
#[derive(Debug)]
struct Player {
name: String,
}
#[derive(Debug)]
struct World {
players: Vec<Rc<RefCell<Player>>>,
current: Rc<RefCell<Player>>,
}
impl World {
fn set_current_player(&mut self, i: usize) {
self.current = Rc::clone(&self.players[i]);
}
fn set_current_player_name(&self, name: &str) {
let mut current = self.current.borrow_mut();
current.name = name.to_string();
}
fn print_pointers(&self) {
for p in self.players.iter() {
println!("{} #pointers: {}", p.borrow().name, Rc::strong_count(p));
}
}
}
// Unsafe pointer
#[derive(Debug)]
struct UnsafeWorld {
players: Vec<Player>,
current: Option<*mut Player>,
}
impl UnsafeWorld {
fn set_current_player(&mut self, i: usize) {
self.current = Some(&mut self.players[i]);
}
fn set_current_player_name(&self, name: &str) {
if let Some(current_ptr) = self.current {
unsafe {
(*current_ptr).name = name.to_string();
}
}
}
fn current_player(&self) -> &Player {
unsafe {
&*self.current.unwrap()
}
}
}
fn main() {
// SAFE
println!("-- SAFE: Rc + RefCell");
let p1 = Rc::new(RefCell::new(Player {
name: "Player 1".to_string(),
}));
let p2 = Rc::new(RefCell::new(Player {
name: "Player 2".to_string(),
}));
let p3 = Rc::new(RefCell::new(Player {
name: "Player 3".to_string(),
}));
let sp2 = Rc::clone(&p2);
let mut world = World {
players: vec![p1, p2, p3],
current: sp2,
};
println!("{:#?}", world);
world.set_current_player(1);
world.set_current_player_name("CHANGE 2");
world.print_pointers();
println!("{:#?}", world);
world.set_current_player(2);
world.set_current_player_name("CHANGE 3");
world.print_pointers();
println!("{:#?}", world);
// UNSAFE
println!("-- UNSAFE: ptr");
let p1 = Player {
name: "Player 1".to_string(),
};
let p2 = Player {
name: "Player 2".to_string(),
};
let mut world = UnsafeWorld {
players: vec![p1, p2],
current: None,
};
world.set_current_player(1);
world.set_current_player_name("CHANGE 2");
println!("{:#?}", world);
println!("Current: {:#?}", world.current_player());
let ref_world = &world;
ref_world.set_current_player_name("REFERENCE");
println!("{:#?}", ref_world);
println!("Current: {:#?}", ref_world.current_player());
move_and_print(world);
}
fn move_and_print(mut world: UnsafeWorld) {
println!("-- PRINT");
//world.set_current_player(0);
world.set_current_player_name("SUB-ZERO");
println!("{:#?}", world);
println!("Current: {:#?}", world.current_player());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment