These are chat archives for rust-lang/rust

23rd
Oct 2016
Arthur
@Biacode
Oct 23 2016 09:11

Hello

use std::collections::{HashMap};

pub trait ContextUnit {}

#[derive(Debug)]
pub struct SimpleContextUnit {}

#[derive(Debug)]
pub struct ComplexContextUnit {}

impl ContextUnit for SimpleContextUnit {}

impl ContextUnit for ComplexContextUnit {}

#[derive(Debug)]
pub struct ContextHolder<T: ContextUnit> {
    units: HashMap<String, T>
}

fn main() {
    let mut units = HashMap::new();
    units.insert("simple".to_owned(), SimpleContextUnit {});
    //    units.insert("complex".to_owned(), ComplexContextUnit {});
    let immut_units = units;
    let holder = ContextHolder {
        units: immut_units
    };
    println!("{:?}", holder);
}

How can I reach polymorphism In this example?
If I uncomment units.insert("complex".to_owned(), ComplexContextUnit {}); it will fail.
Because the map value expecting SimpleContextUnit but getting ComplexContextUnit
In OOP language I can have base class and pass it's type to map value.
How can I do it in rust?

P.S the goal is to have a map units: HashMap<String, T> where T: ContextUnit
And pass as map value any struct that implements ContextUnit
Thanks
Mika Attila
@crumblingstatue
Oct 23 2016 09:22

@Biacode For run-time polymorphism, Rust uses what are called "trait objects". Check out https://doc.rust-lang.org/stable/book/trait-objects.html

In this case, you would use HashMap<String, Box<ContextUnit>.

Arthur
@Biacode
Oct 23 2016 09:24
This message was deleted
Arthur
@Biacode
Oct 23 2016 09:36

In this case, I can't even put the first value

#[derive(Debug)]
pub struct ContextHolder {
    units: HashMap<String, Box<ContextUnit>>
}

fn main() {
    let mut units = HashMap::new();
    units.insert("simple".to_owned(), Box::new(SimpleContextUnit {}));
    //    units.insert("complex".to_owned(), Box::new(ComplexContextUnit {}));
    let immut_units = units;
    let holder = ContextHolder {
        units: immut_units
    };
    println!("{:?}", holder);
}

Got an error:

error[E0308]: mismatched types
  --> src/main.rs:52:16
   |
52 |         units: immut_units
   |                ^^^^^^^^^^^ expected trait ContextUnit, found struct `SimpleContextUnit`
   |
   = note: expected type `std::collections::HashMap<std::string::String, Box<ContextUnit + 'static>>`
   = note:    found type `std::collections::HashMap<std::string::String, Box<SimpleContextUnit>>`
Mika Attila
@crumblingstatue
Oct 23 2016 09:38
@Biacode Type inference is not smart enough in this context. You need to annotate the type like let mut units: HashMap<_, Box<ContextUnit>> = HashMap::new();.
Arthur
@Biacode
Oct 23 2016 09:39
@crumblingstatue Yeap! Thank you.
Arthur
@Biacode
Oct 23 2016 16:54

Any idea how I can make this struct copiable?

#[derive(PartialEq, Debug, Copy, Clone)]
pub struct DockerCredentials {
    api_address: String,
    api_port: i32
}

Ive getting an error because it contains String field...

error[E0204]: the trait `Copy` may not be implemented for this type
 --> src/docker/docker.rs:1:28
  |
1 | #[derive(PartialEq, Debug, Copy, Clone)]
  |                            ^^^^ field `api_address` does not implement `Copy`
Mika Attila
@crumblingstatue
Oct 23 2016 17:00

@Biacode The Copy trait is only meant for types that are safe to make a copy of via memcpy.

String is not safe to "Copy", because it owns data on the heap. And since your type contains a String, it also cannot be safely copied in that manner.

There is another way to make copies of objects, via the Clone trait, which I just noticed you are also deriving. In order to clone something via the Clone trait, you must explicitly call .clone() on the object. You cannot implicitly clone something.

Arthur
@Biacode
Oct 23 2016 17:02
Yeah makes sense, but as far as I know the .clone() is moving operation? no? It means that I will lose copy of struct from original variable
Diggory Blake
@Diggsey
Oct 23 2016 17:03
no, clone() does not affect the thing being cloned
Mika Attila
@crumblingstatue
Oct 23 2016 17:03
@Biacode If you take a look at https://doc.rust-lang.org/std/clone/trait.Clone.html , you can see that it takes &self, which is an immutable borrow.
Diggory Blake
@Diggsey
Oct 23 2016 17:03
you can tell because it takes &self (ie. by reference) rather than self (moving it)
Arthur
@Biacode
Oct 23 2016 17:05

Very nice! Thanks for documentation page

#[derive(PartialEq, Debug, Clone)]
pub struct DockerCredentials {
    api_address: String,
    api_port: i32
}

fn main() {
    let credentials = DockerCredentials {
        api_address: "10.18.3.35".to_owned(),
        api_port: 2375
    };
    let clone = credentials.clone();
    println!("{:?}", credentials);
    println!("{:?}", clone);
}

Indeed.