30 Days of Rust - Day 17
Tags: rust, learning, beginners
Published: 2023-05-13 17:27:13

Hello folks,
I do apologise for missing the last couple of days. I had a lot of work to finish up before taking some PTO and I couldn't quite manage the cognitive load of the blog. But now I'm back and ready to finish the 13 of the 30 days. Today we'll be diving into structs in more detail. I briefly introduced this topic back on [day 4](https://dev.to/johnnylarner/30-days-of-rust-day-4-120n).

## Yesterday's questions answered
- Yes, Rust doesn't treat slice references any differently

## Today's open questions
- How many pointer can Rust automatically dereference?

## Con- and destructuring structs
The way we instantiate structs in Rust is pretty typical. Take a simple user `struct`:
```rust
struct User {
	active: bool,
	username: String,
	email: String,
	sign_in_count: u64,
}
```

The most basic way to instantiate a user `struct` would be to pass values mapped to its `fields` :
```rust
let inactive_johnny = User {
	active: false,
	username: String::from("johnnylarner"),
	email: String::from("johnnylarner@gmail.com"),
	sign_in_count: 100,
};
```

Now sometimes we make have default values for particular `fields` and thus often may want to parameterise only a subset of `fields` in a function:

```rust
fn build_user(email: String, username: String) -> User {
	User {
		active: true,
		email,
		username,
		sign_in_count: 1,
	}
}
```

Now Rust provides developers with a so-called *init shorthand*  syntax. Similar to object deconstruction in JavaScript, when we have an in-scope variable that matches a declared `field` name of a `struct`, we don't need to use the map syntax. Hence in the above function we are able to simple pass the `email` and `username` arguments as is.

Rust has also incorporated another JavaScript-esque feature. Take a look and see if you can see what I mean:
```rust
let activated_johnny = User {
	active: true,
	..inactive_johnny
}
```

>Give yourself a pat on the back if you could see JavaScripts `...` spread operator here. Or perhaps the Pythonistas see a `**` key value unpack operator here. These operators will make shallow copies of the values that are yielded by the operator. 

The *update* syntax in Rust allows us to *move* values from one instance of a struct to another. What does it *moving* mean again? It means we make the new variable `activated_johnny` the owner of the data previously owned by `inactive_johnny`.  Thus, now trying to reference `inactive_johnny` will cause your code to not compile. One side note here, if all values were actually primitives stored on the stack, then you would actually by *copying* rather than *moving* the data. Move semantics here explain why we use the verb *update* rather than *copy* to describe this syntax.

Finally, I should add that `structs` are not required to have any fields:
```rust
struct AlwaysEqual;

let always_eq = AlwaysEqual; // This compiles.
```

### What should structs own?
For young `Rustaceans` like myself `ownership` can be relatively easy to comprehend on a component level. But advanced and optimised system design Rust must of course include a sophisticated `ownership` designs, where serious thought is given to the `lifetime` of data on the heap and which parts of the system should read and write that data.

In the context of structs I'm going to accept that the naïve solution for now is simply to say:
> A struct should own its own data.

Later in the blog series, we'll challenge this hypothesis.

## Named tuples??
Experienced Pythonistas should know about a lightweight alternative to classes: `namedtuples`. Now Rust has something similar, we'll at least at first glance. `tuple structs` are a quick way to group related data. They are less verbose than a `struct` and may often suffice if data we are trying to represent is conceptually concise. Now this comparison with Python is actually quite *misleading* as `tuple structs` do not contain any field names:
```rust
struct Point(i32, i32, i32);
struct Color(i32, i32, i32);
```

Note how we wrap the declaration in parentheses (not curlies) and end it with a semi-colon.

## Viewing your beautiful structs
Must like in Python, printing your `struct` to the console will not really help you see what's stored in your `struct`. In Python, you'll get a memory address. In Rust your code just won't compile. 

The quick fix to this in Rust is to have your `struct` derive the `Debug` trait:
```rust
#[derive(Debug)]
struct Rectangle {
	width: u32,
	height: u32,
}
```

With your struct declaration fixed up, you now have 3 options on how to view it: 
```rust
let rect = Rectangle {width: 10, height: 100 }

println!("My ugly rectangle: {:?}", rect)
println!("My pretty rectangle: {:#?}", rect)
dbg!("My ugly rectangle: {:?}", rect)

```

I'd suggest testing out the code locally to see the results. But summarised:
- `:?` will give you an inline view of the struct
- `:#?` will give each field on a new line and indented correctly
- `dbg!` the debug macro takes (and returns) ownership of your `struc` and pretty prints it along with its line number in the source file.

## Methods and associated functions
We can declare methods for `strucs` by using the `impl` keyword. Methods in the `impl` block are declared like functions: using the `fn` keyword. In theory, you can use an arbitrary number of `impl` blocks to declare methods (and traits). But typically you'll only want to use one `impl` block for all methods. 

Methods borrow the `struct`'s data using the `&self` as the first parameter. This are called by calling the `.`  operator on an instance of the `struct`.  The `&self` type also indicates to us that a method is read-only. If we want to edit the underlying data of a struct, we need the `&mut self` operator instead.

We've already seen how creating `structs` usually takes place via an associated function. I've definitely mistakenly called these `static` methods which is incorrect:
```rust
let my_string = String::from("My string");
```

Here `from` is an associated function whose return type is `Self`, a reference to the type implementing the method.


### Calling methods on pointers to structs
There may be cases when you actually have a pointer to a struct. In C-based languages, there is a specific operator to call methods on pointers: `->`. If you call a method on a pointer to a struct, Rust will automatically follow the pointer and call the method on the struct. This is known as automatic dereferencing.