How to unpack a struct in another struct in rust? - rust

Can you do something like this?
struct Person {
hp: i32
}
struct EvolvedPerson {
hp: i32,
evolved_stat: i32,
}
let p1 = Person { hp: 10 }
let p2 = EvolvedPerson { ..p1, extra_stat: 20 }
Where the p2 now has the hp of p1.

Can you do something like this?
No. You can only unpack structs of the same type.

As a possible solution you can deconstruct p1 and then provide variables to p2 by shorter syntax:
struct Person {
hp: i32
}
struct EvolvedPerson {
hp: i32,
evolved_stat: i32,
}
let p1 = Person { hp: 10 };
let p2 = {
let Person { hp } = p1;
EvolvedPerson { hp, extra_stat: 20 }
};

The idiomatic way would be to include the Person struct instead:
struct Person {
hp: i32,
}
struct EvolvedPerson {
person: Person,
evolved_stat: i32,
}
let p1 = Person { hp: 10 }
let p2 = EvolvedPerson { person: p1, extra_stat: 20 }
That way it is also trivial to implement AsRef<Person> for EvolvedPerson and thus you can use Person methods with EvolvedPerson too.

Related

Rust pushing to vector with borrow_mut

Let's have a struct containing a vector of cities and a new_city function adding City to the vector. However, I got BorrowMutError which makes sense.
What should I do so I can call new_city multiple times (see below)?
I would need to drop the borrow_mut reference in the new_city function but I don't know how.
//use std::rc::Rc;
use std::cell::RefCell;
use std::cell::Ref;
pub struct Simulation{
cities: RefCell<Vec<City> >,
}
impl Simulation{
pub fn new() -> Simulation
{
Simulation{
cities: RefCell::new(Vec::new()),
}
}
pub fn new_city(&self, name: &'static str) -> Ref<City> {
let city = City::new(name);
self.cities.borrow_mut().push(city);
Ref::map(self.cities.borrow(), |vec| vec.last().unwrap())
}
}
#[derive(Debug, Copy, Clone)]
pub struct City {
name: &'static str,
}
impl City{
pub fn new(name: &'static str) -> City {
City { name: name, }
}
}
fn main(){
let mut simulation = Simulation::new();
let prg = simulation.new_city("Prague");
let brn = simulation.new_city("Brno");
let pls = simulation.new_city("Pilsen");
println!("{:?}", prg);
}
EDIT: Next use
Then I need prg and brn cities to add road between them with API (another vector in Simulation)
pub fn new_road(&self, from: &City, to: &City, time: i32) -> &Road {
//Adding information about road to simulation
}
let d1 = simulation.new_road(&prg, &brn, 120);
Therefore I cannot drop prg or brn.
You could simply introduce a new scope at the end of which the BorrowMut gets dropped:
pub fn new_city(&self, name: &'static str) -> Ref<City> {
{
let city = City::new(name);
self.cities.borrow_mut().push(city);
}
Ref::map(self.cities.borrow(), |vec| vec.last().unwrap())
}
But you can't hold on to the Refs across new_city calls either.
fn main() {
let mut simulation = Simulation::new();
let prg = simulation.new_city("Prague");
drop(prg);
let brn = simulation.new_city("Brno");
}
You might want to wrap the cities in Rcs to be able to hold on to them across pushes:
pub struct Simulation{
cities: RefCell<Vec<Rc<City>>>,
}
//...
impl Simulation {
//...
pub fn new_city(&self, name: &'static str) -> Rc<City> {
{
let city = Rc::new(City::new(name));
self.cities.borrow_mut().push(city);
}
self.cities.borrow().last().unwrap().clone()
}
}

Is RefCell an appropriate workaround to borrow two mutable elements from a vector?

Consider this toy example of "fighting" two random "players":
#[derive(Clone)]
struct Player {
name: String,
health: i32,
attack: i32,
}
fn fight(player_a: &mut Player, player_b: &mut Player) {
player_a.health -= player_b.attack;
player_b.health -= player_a.attack;
}
fn main() {
// Create Vector of 100 new players
let players: Vec<Player> = vec![
Player {
name: String::new(),
health: 100,
attack: 5,
};
100
];
// Pick two "random" indices
let i1 = 19;
let i2 = 30;
fight(&mut players[i1], &mut players[i2]); // Error!
}
This code will not work as the fight function takes two mutable references to elements of the same players vector.
My ugly workaround currently looks like the following, using RefCell:
use std::cell::RefCell;
let mut players: Vec<RefCell<Player>> = vec![];
for _ in 0..100 {
players.push(RefCell::new(Player {
name: String::new(),
health: 100,
attack: 5,
}));
}
fight(&mut players[i1].borrow_mut(), &mut players[i2].borrow_mut());
I'd like to know if there's a more efficient way of doing this to avoid the extra overhead of RefCell? Can I leverage split_at_mut somehow?
It’s possible to use split_at_mut to borrow both exclusively:
#[derive(Clone)]
struct Player {
name: String,
health: i32,
attack: i32,
}
fn fight(player_a: &mut Player, player_b: &mut Player) {
player_a.health -= player_b.attack;
player_b.health -= player_a.attack;
}
fn get2<T>(arr: &mut [T], a: usize, b: usize) -> (&mut T, &mut T) {
use std::cmp::Ordering;
let (sw, a, b) = match Ord::cmp(&a, &b) {
Ordering::Less => (false, a, b),
Ordering::Greater => (true, b, a),
Ordering::Equal =>
panic!("attempted to exclusive-borrow one element twice"),
};
let (arr0, arr1) = arr.split_at_mut(a + 1);
let (ea, eb) = (&mut arr0[a], &mut arr1[b - (a + 1)]);
if sw {
(eb, ea)
} else {
(ea, eb)
}
}
fn main() {
// Create Vector of 100 new players
let mut players: Vec<Player> = vec![
Player {
name: String::new(),
health: 100,
attack: 5,
};
100
];
// Pick two "random" indices
let i1 = 19;
let i2 = 30;
let (p1, p2) = get2(&mut players, i1, i2);
println!("{} ({} HP) vs {} ({} HP)",
p1.attack, p1.health, p2.attack, p2.health);
fight(p1, p2);
println!("{} ({} HP) vs {} ({} HP)",
p1.attack, p1.health, p2.attack, p2.health);
}
You can change fight method like next:
#[derive(Clone)]
struct Player {
name: String,
health: i32,
attack: i32,
}
fn fight(players: &mut [Player], player1_index: usize, player2_index: usize) {
players[player1_index].health -= players[player2_index].attack;
players[player2_index].health -= players[player1_index].attack;
}
fn main() {
// Create Vector of 100 new players
let mut players: Vec<Player> = vec![
Player {
name: String::new(),
health: 100,
attack: 5,
};
100
];
// Pick two "random" indices
let i1 = 19;
let i2 = 30;
fight(&mut players, i1, i2);
}
Or you can try to workaround this issue with Option:
#[derive(Clone)]
struct Player {
name: String,
health: i32,
attack: i32,
}
fn fight(player_a: &mut Player, player_b: &mut Player) {
player_a.health -= player_b.attack;
player_b.health -= player_a.attack;
}
fn main() {
// Create Vector of 100 new players
let mut players: Vec<Option<Player>> = vec![
Some(Player {
name: String::new(),
health: 100,
attack: 5,
});
100
];
// Pick two "random" indices
let i1 = 19;
let i2 = 30;
let mut player1 = players[i1].take().unwrap();
let mut player2 = players[i2].take().unwrap();
fight(&mut player1, &mut player2);
players[i1].replace(player1);
players[i2].replace(player2);
}
Or if you really need 100% performance, you can try to dig deeper into unsafe raw pointers. But you should think twice.

How do I enable a struct instance to have fields with different types based on the enum value passed in?

I'd like to create different 'flavors' of a struct. This means that depending on which enum I'm passing into the structure's constructor I'll get a version of the structure whose 'flavor' trait's type depends on that enum value. I know that I must specify a single type for my structure's fields, but what if I want that type to change based on the value I'm passing into the constructor? Can I only do something like this using traits?
pub struct ChocolateFlavor {
coco: u32,
}
pub struct VanillaFlavor {
bean: u32,
}
pub struct IceCream {
flavor: (), //should be able to be ChocholateFlavor or VanillaFlavor
}
pub enum Flavors {
Vanilla,
Chocholate,
}
impl IceCream {
pub fn new(input_type: Flavors) -> Self {
match input_type {
Flavors::Vanilla => input = VanillaFlavor,
Flavors::Chocholate => input = ChocolateFlavor,
}
let mut new_icecream = LabeledInput { flavor: input };
new_icecream
}
}
fn main() {
//give me a chocholate ice cream
let mut my_chocholate = IceCream::new(Flavors::Chocolate);
//give me a vanilla ice cream
let mut my_vanilla = IceCream::new(Flavors::Vanilla);
}
You can use enums with values
enum FlavorData {
ChocolateFlavor { coco: u32 },
VanillaFlavor { bean: u32 },
}
pub struct IceCream {
flavor: FlavorData,
}
pub enum Flavors {
Vanilla,
Chocolate,
}
impl IceCream {
pub fn new(input_type: Flavors) -> Self {
let flavor = match input_type {
Flavors::Vanilla => FlavorData::VanillaFlavor { bean: 10 },
Flavors::Chocolate => FlavorData::ChocolateFlavor { coco: 20 },
};
IceCream { flavor: flavor }
}
}
fn main() {
//give me a chocholate ice cream
let mut my_chocolate = IceCream::new(Flavors::Chocolate);
//give me a vanilla ice cream
let mut my_vanilla = IceCream::new(Flavors::Vanilla);
}

How do I declare a "static" field in a struct in Rust?

How do I declare a "static" field in a struct in Rust, preferably with a default value:
struct MyStruct {
x: i32, // instance
y: i32, // instance
my_static: i32 = 123, // static, how?
}
fn main() {
let a = get_value();
if a == MyStruct::my_static {
//...
} else {
//...
}
}
You can declare an associated constant in an impl:
struct MyStruct {
x: i32,
y: i32,
}
impl MyStruct {
const MY_STATIC: i32 = 123;
}
fn main() {
println!("MyStruct::MY_STATIC = {}", MyStruct::MY_STATIC);
}
Rust does not support static fields in structures, so you can't do that. The closest thing you can get is an associated method:
struct MyStruct {
x: i32,
y: i32,
}
impl MyStruct {
#[inline]
pub fn my_static() -> i32 {
123
}
}
fn main() {
let a = get_value();
if a == MyStruct::my_static() {
//...
} else {
//...
}
}
You can't declare a field static in a struct.
You can declare a static variable at module scope like this :
static FOO: int = 42;
And you can't have a static variable mutable without unsafe code : to follow borrowing rules it would have to be wrapped in a container making runtime borrowing checks and being Sync, like Mutex or RWLock, but these cannot be stored in static variable as they have non-trivial constructors.

Several implementations of the Add trait for the same type

I'm trying to do something very simple like that:
fn main() {
#[deriving(Show)]
struct A {
a: int
}
impl Add<A, A> for A {
fn add(&self, other: &A) -> A {
A { a: self.a + other.a }
}
}
impl Add<int, A> for A {
fn add(&self, v: &int) -> A {
A { a: self.a + *v }
}
}
let x = A { a: 10 } + A { a: 20 };
println!("x: {}", x);
}
Rust compile doesn't like my code and says:
src/sandbox.rs:20:12: 20:37 error: multiple applicable methods in scope [E0034]
src/sandbox.rs:20 let x = A { a: 10 } + A { a: 20 };
^~~~~~~~~~~~~~~~~~~~~~~~~
src/sandbox.rs:8:7: 10:8 note: candidate #1 is `main::A.Add<A, A>::add`
src/sandbox.rs:8 fn add(&self, other: &A) -> A {
src/sandbox.rs:9 A { a: self.a + other.a }
src/sandbox.rs:10 }
src/sandbox.rs:14:7: 16:8 note: candidate #2 is `main::A.Add<int, A>::add`
src/sandbox.rs:14 fn add(&self, v: &int) -> A {
src/sandbox.rs:15 A { a: self.a + *v }
src/sandbox.rs:16 }
Ultimately I want to add an int to my type A like that:
let x: A = A { a: 10 } + A { a: 20 };
let y: A = A { a: 10 } + 20i;
let z: A = A 10i + { a: 20 };
What is the best approach?
Update:
YES, you can implement this now!
How? In similar manner to below:
use std::ops::Add;
#[derive(Debug)]
struct A {
a: i32,
}
impl Add<i32> for A {
type Output = A;
fn add(self, _rhs: i32) -> A {
A { a : self.a + _rhs }
}
}
impl Add<A> for A {
type Output = A;
fn add(self, _rhs: A) -> A {
A { a : self.a + _rhs.a }
}
}
fn main() {
let x = A { a: 10 } + A { a: 20 };
let y = A { a: 40 } + 2;
println!("x: {:?}\ny: {:?}", x, y);
}
Explanation. See when you write
let x = A { a: 10 } + A { a: 20 };
Rust looks up all Add traits implemented for A. Problem is because there are two defined: impl Add<A, A> for A and impl Add<int, A> for A Rust is 'unsure' which one to take. Don't quote me on this, because Rust compiler internals are not my cup of tea, but I think Rust team wanted to avoid paying the price of having multi dispatch.
Your solution is to either:
A) Add another trait like in this answer that will do the addition for you like in example given.
B) Wait for associative types to land, which is the better bet. ( Issue #17307 )
C) Give up on impl Add<int, A> for A.
I think what you want is multi-dispatch which should be landing soon. See this RFC #195 for details.

Resources