Skip to content

Commit

Permalink
Adding trait_vs_enum_in_vec
Browse files Browse the repository at this point in the history
  • Loading branch information
simsekgokhan committed Jan 22, 2024
1 parent 10e9fb9 commit bf95c11
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/copy_vs_move.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#[test]
fn ex1_ww() {
fn ex1() {
// a. copy
let x = 5;
let y = x; // !! copied, not moved
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ mod unit_integration_tests;
mod temp;
mod type_;
mod trait_simple_vs_enum;
mod trait_vs_enum_in_vec;
mod traits_when_to_use_dyn_dispatch;
mod traits_object_vs_struct_obj;
mod traits;
Expand Down
94 changes: 94 additions & 0 deletions src/trait_vs_enum_in_vec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// 1. Polymorphism with trait
// New types and trait are decoupled/independent
trait Shape { fn area(&self) -> i32; }

struct Square { x: i32 }
impl Shape for Square {
fn area(&self) -> i32 { self.x * self.x }
}

struct Rect { w: i32, h: i32 }
impl Shape for Rect {
fn area(&self) -> i32 { self.w * self.h }
}

// 2. Polymorphism with enum
// Types and enum are bound together
enum Shape_{
Square { x: i32 },
Rect { w: i32, h: i32},
}

impl Shape_ {
fn area(&self) -> i32 {
match self {
Self::Square {x} => x * x,
Self::Rect {w, h} => w * h,
}
}
}

#[test]
fn ex1() {
// 1. trait
let s1: Box<dyn Shape> = Box::new(Square {x: 4});
let vec_of_traits = vec![
s1,
Box::new(Rect {w: 5, h: 6}) as Box<dyn Shape> // s2
];
crate::print_type_of("vec_of_traits[0]", &vec_of_traits[0]);
// alloc::boxed::Box<dyn rust_book_minigrep::temp::Shape>
println!("-- {}", vec_of_traits[0].area());

// 2. enum
let vec_of_enums = vec![Shape_::Square { x: 8 }];
crate::print_type_of("vec_of_enums[0]", &vec_of_enums[0]);
// rust_book_minigrep::temp::Shape_
println!("-- {}", vec_of_enums[0].area());
let xx = match vec_of_enums[0] {
Shape_::Square {x} => x,
Shape_::Rect {w, ..} => w
};

assert_eq!(xx, 8);
}
// https://stackoverflow.com/questions/52240099/should-i-use-enums-or-boxed-trait-objects-to-emulate-polymorphism
// - One of the big differences between using traits and enums is their
// extensibility. If you make Axes an enum, then the two options are
// hardcoded into the type. If you want to add some third form of axis,
// you'll have to modify the type itself, which will probably involve a lot
// of modifications to the code with uses Axes (e.g. anywhere you match on an
// Axes will probably need to be changed)
// On the other hand, if you make Axes a trait, you can add other types of
// axes by just defining a new type and writing an appropriate implementation,
// without modifying existing code at all. This could even be done from outside
// of the library, e.g. by a user.
//
// - The other important thing to consider is how much access you need to the
// internals of the structs. With an enum, you get full access to all the data
// stored within the struct. If you want to write a function which can operate
// on both Coordinate and Quaternion using a trait, then the only operations
// you will be able to perform are those described in the Axes trait
// (in this case Shift and Fold). For instance, giving the implementation of
// Axes you gave, there would be no way for you to simply retrieve the (X,Y,Z)
// tuple via the Axes interface. If you needed to do that at some point, you
// would have to add a new method.
//
// - enums can be stored directly on the stack, while a boxed trait will always
// require the heap. That is, enums are cheap to create, but boxed traits are not.
//
// - an enum instance will always be as big as its biggest variant (plus a
// discriminant in most cases), even if you store mostly small variants.
// This would be a problem in a case like this:
// enum Foo {
// SmallVariant(bool),
// BigVariant([u64; 100]),
// }
// If you were to store N instances of this type in an vector, the vector
// would always need N*(100*sizeof::<u64> + sizeOfDiscriminant) bytes of memory,
// even when the vector only contains SmallVariants.
// If you were using a boxed trait, the vector would use N * sizeOfFatPointer
// == N * 2 * sizeof::<usize>
// Just for completeness, if you box just the array inside BigVariant,
// the vector ends up the same size as the boxed trait version, but you
// still get the other advantages of enum aside from stack allocation
2 changes: 1 addition & 1 deletion src/traits_return_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn get_closure_() -> impl Fn(i32) -> i32 {
#[test]
fn ex1() {
let xx = foo();
crate::print_type_of(&xx); // i32
crate::print_type_of("xx", &xx); // i32
let yy = foo_();
// assert_eq!(xx, 5); // todo
}
4 changes: 2 additions & 2 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ pub fn pp<T: std::fmt::Display>(num: T) -> String {
.collect::<std::result::Result<Vec<&str>, _>>().unwrap().join("_")
}

pub fn print_type_of<T>(_: &T) {
println!("--- type of: {}", std::any::type_name::<T>())
pub fn print_type_of<T>(name: &str, _: &T) {
println!("--- type of {}: {}", name, std::any::type_name::<T>())
}

0 comments on commit bf95c11

Please sign in to comment.