DEV Community

Cover image for Rustlings V (Structs, enums)
Blitty
Blitty

Posted on

Rustlings V (Structs, enums)

Lets go

Structs 🥸

Like C structs these types are a set of different fields with a type and it can be used for almost everything. A cool feature that Rust have, are methods for structs which is different from a function.

"Normal" structs

Also a cool thing about Rust, that is like Javascript (ECMA6), the spread operator, which let us take the values from a struct and place them into another one in the same fields. Another feature like in ECMA6 is that when you have variables with the same name as a property from a struct, you can use the name instead of variable and value.

// creating an Animal struct
struct Animal {
  name: String,
  age: u8,
  death: bool,
  type: String,
};

let name = "Josefina";

// instanciate a new Animal
let bonnie = Animal {
  name: String::from("Bonnie"),
  age: 5,
  death: false,
  type: String::from("dog"),
};

// normal re-use from a struct to a new one
let josefina = {
  name, // instead of name: name (which is redundant)
  // also don't do these, this way, do it with the spread operator
  age: bonnie.age,
  death: bonnie.age,
  type: bonnie.type,
};

// re-use properties from a struct rust way
let guru = Animal {
  name: String::from("Guru"),
  age: 9,
  ..bonnie,
};
Enter fullscreen mode Exit fullscreen mode

Tuple struct

You can use tuples as a struct instead of a normal struct. So instead of having a struct Coordinates fields with names x, y. We can have a struct tuple, which has x and y without the names.

struct Coordinates_like_MEH { X: i32, Y: i32 };
// do this 
struct Coordinates (i32, i32);

let origin = Coordinates(0, 0);
println!("x: {}, y: {}", origin.0, origin.1);
Enter fullscreen mode Exit fullscreen mode

This is useful because sometimes the name of the attributes annoy more than helps.

Unit-Like structs

This are structs that does not have fields. I mean this is cool, because if you have something you don't know what it gives you, you can use this structs. (We will see on later posts, because is chapter 10)

Also, remember: Be careful with the context!!!! Because of ownership OwO

An important thing about structs, and in general, is that when you use println!() and types like integer, float, etc. they have implementd a Display method.
Structs does not have that, so you need to implement it or use Debug that can be used to print data of a field or the hole struct.

#[derive(Debug)] // u need this otherwise you won't be able to use debug
struct Meeeep {
  wtf_level: i32,
};

let meep007 = Meeeep {
  wtf_level: 10000,
};

println!("meep007: {:?}", meep007);
// output: meep007: Meeeep { wtf_level: 10000, }

println!("meep007: {:#?}", meep007);
// output:
// meep007: Meeeep {
//   wtf_level: 10000,
// }

dbg!(&meep007);
// output:
// [src/main.rs:20] &meep007: Meeeep {
//   wtf_level: 10000,
// }

let _miau = Meeeep {
  wtf_level: dbg!(-10 * meep007.wtf_level / 9086),
};
// output:
// [src/main.rs:25] -10 * meep007.wtf_level / 9086 = -11
Enter fullscreen mode Exit fullscreen mode

Methods

The difference between a function and a method is that, a method is part of a struct, so it is defined on the context of that struct.

struct Animal {
  name: String,
  age: u8,
  death: bool,
  type: String,
};

// how to create a method
impl Animal {
  fn say_guau (&self) {
    println!("{} says FOLOOOOWWW MEEEEEE!!!", self.name);
  }
}

let name = "Josefina";

let bonnie = Animal {
  name: String::from("Bonnie"),
  age: 5,
  death: false,
  type: String::from("dog"),
};

say_guau(bonnie);
Enter fullscreen mode Exit fullscreen mode

IMPORTANT!!!
&self means a variable called self of type Self. And what is self?? Well... I think it describes its self uwu, is a reference to an instance of type (in this case) Animal.

If you read the docs, you will see that it talks about -> which I am not going to cover. Sorry UwU

To end with structs, you can use multiple impl if you want, but I don't see why you would use two impl for the same struct when you can have everything in one.

Enums 🤪

At this point, I am tired, I want to sleep and have dinner :v butttt I am going to finish the post. So:

go

Enums are as you can think of, enumerators, this means that you have a set of values that can be matched. Also enums can have methods like structs.

#[derive(Debug)]
enum AnimalType {
  DOG,
  CAT,
  LION,
  IDK,
}

impl AnimalType {
  fn wtf_is_this_shit(&self) {
    println!("Tricky tricky lemons q-easy!!! {:?}", AnimalType::IDK);
  }
}

fn main() {
  let owo = AnimalType::DOG;
  owo.wtf_is_this_shit();
}
Enter fullscreen mode Exit fullscreen mode

You can specify the type of a field:

enum AnimalType {
  DOG,
  CAT,
  LION,
  IDK(i32),
}
Enter fullscreen mode Exit fullscreen mode

In the book there is an explanation about null values which I recommend reading.

But what it says it that in Rust you cannot have null values never!! There is an enum Option<T> being T a variable type which is used to handle not havind null values. This enum has None, and Some(T). Also T is used for generalization, we will see it on another post.

Take in consideration that you cannos operate with a variable which is Option<T> and a T variable type. There are ways to convert Option<T> on T. docs

let some_number = Some(5);
let num = 9;

let sum = num + some_number; // Will fail because Option<i32> != i32

// using None
// we are saying that it will be an Option which type is String but we don't know its value right now.
let some_value: Option<String> = None;
Enter fullscreen mode Exit fullscreen mode

Using match with enums. match is a useful and important keyword for enum, becuase is the way of having multiple actions depending on which one is true.

fn main() {
  enum Drink {
    WATER,
    BEER,
    COKE,
    WINE,
  }

  match Drink::WATER {
    Drink::WATER => println!("Nice :D"),
    Drink::BEER => println!("IDIOT^4"),
    Drink::COKE => println!("IDIOT^10"),
    Drink::WINE => println!("IDIOT^0"),
  }
}
Enter fullscreen mode Exit fullscreen mode

The difference between match and if, is that "if" needs to have a boolean condition.
When match is executes, it matches the value of the code associated with the pattern given, if the pattern does not match the value, it continues to the next arm, otherwise it executes what is on tje right side of "=>" (the arm).

Match patterns are evaluated in order!!!! So be caredul when using the "catch-all" pattern.

If you need to execute multiple statements, use {} (curly braces).

The code related with each arm (option) is an expression, and if you remember, that means that it "returns" that value.

Using match with Option<T>.

// docs example
fn main() {
  fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
      None => None,
      Some(i) => Some(i + 1),
    }
  }

  let five = Some(5);
  let six = plus_one(five); // we will get 6 because Some(i) == Some(5)
  let none = plus_one(None); // We will get None, because None == None
}
Enter fullscreen mode Exit fullscreen mode

In case you need a pattern like an "else", because you don't need to check for 1000 cases that are the same code, you use the catch-all patterns. You can use or not the value of the "catch-all" pattern, lets see:

let dice_roll = 9;

// handling catch-all with a variable to use it, becuase be want
match dice_roll {
  3 => println!("haha"),
  7 => println!("hehe"),
  other => println!("This is s* {}", other),
}

// not handling cath-all
match dice_roll {
  3 => println!("haha"),
  7 => println!("hehe"),
  _ => (),
}
Enter fullscreen mode Exit fullscreen mode

Lastly there is something called if let that is used when we want to match only one pattern and we don't care about them.

// docs example
fn main() {
    let config_max = Some(3u8);
    if let Some(max) = config_max {
        println!("The maximum is configured to be {}", max);
    }

  // equals
    match config_max {
        Some(max) => println!("The maximum is configured to be {}", max),
        _ => (),
    }
}

Enter fullscreen mode Exit fullscreen mode

I think you can use if let when you have a huge enum with a lot of things and you just need to find one pattern and with the other don't need to do anything.

So this is all... for now I wish you a good day!!


Follow me!

🐦 Twitter
🐙 GitHub
👥 LinkdIn


Top comments (0)