30 Days of Rust - Day 29
Tags: rust, devjournal, learning
Published: 2023-07-04 18:47:02

Good evening folks,
We're nearly there. Two posts left. I've got a final kick of energy to push through. Today we'll be zooming over some of Rust's object oriented, pattern matching and unsafe features.

## Yesterday's questions answered
*No questions to answer*

## Today's open questions
*No open questions*

## Is Rust object-oriented?
There are many ways to define whether a programming language is object-oriented or not. When it comes to Rust, a useful way to think about this is to ask yourself: what are OO languages designed to achieve and how do they do this?

### Encapsulating logic
Objects allow you to separate and encapsulate different parts of your code. Rust's features definitely support encapsulation. `Structs` and `impl` blocks allow programmers to write distinct code that is separated from other bits of code ✅

### Objects as data storage
Often object-oriented languages express human concepts through grouping data in objects. In Rust `enums` and `structs` are also a great way to group conceptually similar data in one place. ✅

### Reusing and types through inheritance
The most obvious missing aspect so far for new Rust developers is the absence of inheritance. In many programming languages, objects expressed as subclasses are able to inherit from parent classes. Inheritance can:
1. Reduce code duplication by defining common code at the parent level
2. Increase the breadth of composability as subclasses share their parent's type.

We know there are no classes in Rust, and that `structs` cannot inherit from each other. But this doesn't mean Rust doesn't have mechanisms to tackle the issues noted above.

`trait` objects provide a way of declaring default behaviour. If a `trait` implements a method and a `struct` implements that `trait` , the `struct` will take on the default implementation. 

What's more `traits` also grant programmers access to typed interfaces across different structs. If a function or struct field has a `trait` object as its type definition, it will accept any `struct` that implements that trait. `trait` objects incur a small runtime penalty. As we don't know at compile time what all the types could be (imagine you're writing a library that would allow users to make their own components), `trait` objects are stored on the heap. This means we need to access them via a pointer at runtime to find the method we want to call. This is known as *dynamic dispatch*.

## Match, match, match
When learning Rust, `match` statements are one of the first novel features you come across. What's particularly cool about `match` is that the compiler will often force you to exhaustively match all possible outcomes. This catches bugs before they even have a chance to compile.

`match` arms require so-called *refutable* patterns, patterns that can match or not match. *Irrefutable patterns* are most commonly seen in variable assignment:
```rust
let x = 5;
```

### Nested matches
If you're working with nested `enums` or `structs`, you can use `match` patterns beyond the root level of the data structure:
```rust
enum MarriageStatus {
	Single,
	MarriedTo(String),
	Divorced,
}

struct TaxProfile {
	name: String,
	age: i32,
	marriage_status: MarriageStatus,
}

fn main() {
	let profile = TaxProfile {
	name: String::from("Max Mustermann"),
	age: 30,
	marriage_status: MarriageStatus::MarriedTo(String::from("Jane Doe")),
	};

match profile {
	TaxProfile {
		marriage_status: MarriageStatus::MarriedTo(spouse),
		..
	} => {
		println!("Congrats, you and {spouse} get a tax break.");
	}
	_ => println!("Only married people are worthy of tax breaks!"),
	}
}
```

Here we want to print a different message based on whether a given tax profile gets a tax break or not. This code demonstrates several more advanced features of the `match` system:
1. `struct` matching requires you to temporarily instantiate a `struct` and compare the relevant fields
2. Irrelevant fields can be ignored using the range operator `..`. This is useful when we have more than one other field we want to ignore.
3. We can also match based on the inner field's value. This can also be used in the result of the match arm.

### Match guards and bindings
In the above example the first arm of the `match` statement matched to an enum. If we wanted to only match if your spouse has a specific name, we'd also have to introduce a match guard:
```rust
match profile {
	TaxProfile {
		marriage_status: MarriageStatus::MarriedTo(spouse),
		..
	} if spouse == "Angela Merkel" => {
		println!("Congrats, you and Angie get a tax break.");
	}
	_ => println!("Only married people are worthy of tax breaks!"),
	}
}
```

In this case, only the spouse of Angela Merkel would get a tax break. What if we wanted to give tax breaks to old people too? We could specify a range for the match arm and use a binding to extract the result. We can print this to the screen to give the user a personalised response:

```rust
match profile {
	TaxProfile {
		marriage_status: MarriageStatus::MarriedTo(spouse),
		..
	} if spouse == "Angela Merkel" => {
		println!("Congrats, you and Angie get a tax break.");
	}
	TaxProfile {
		age: boomer_age @ 60..=79,
		..
	} => println!("Even at age {boomer_age} you matter to society, have a tax break."),
	_ => println!("Sorry, no tax break for you."),
}
```


## What is unsafe code?
Until now in these blog posts we've only talked about safe Rust code. Safe Rust code is effectively any code that meets the Rust compiler's ownership, borrowing and type rules. We call the code safe because we know that this code will not suffer from invalid pointers, null values, unintended side effects or security issues posed by incorrect pointer addresses.

Unsafe code is the inverse: we no longer get any guarantees from Rust that our program upon submission to the compiler won't have any of these issues at runtime. The trade off is that we no longer have to adhere to all of the compiler's rule. One example might be having more than one mutable reference in scope at any given time:
```rust
fn split_as_mut(values: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
	let len = values.len();
	let ptr = values.as_mut_ptr();
	
	assert!(mid <= len);
	
	unsafe {
		(
			slice::from_raw_parts_mut(ptr, mid),
			slice::from_raw_parts_mut(ptr.add(mid), len - mid),
		)
	}
}
```

You can see we use the `unsafe` keyword to declare a block in which we can execute unsafe code. What makes this code unsafe is calling the associated function `from_raw_parts_mut` as this function is declared as `unsafe`. To make a function `unsafe` you can prepend the function declaration with the `unsafe` keyword.

Beyond the function being `unsafe`, we can tell from our function's return type that both calls `from_raw_parts_mut` a mutable reference for our array into scope. This is not permitted by the Rust compiler in a `safe` context. But was the code is `unsafe`, we don't have any issues when submitting our code.  

The `split_as_mut` function is considered as an *acceptable* example of using `unsafe Rust` as:
1. The `unsafe` part of the code is isolated and wrapped in a `safe` API. This makes calling the function `safe` regardless of where you call it.
2. We manually assert that the pointers we create will point to a valid location in memory for the data structure we're trying to access
3. Both pointers access unique subsets of the array.

## Multilingual Rust
We can also declare Rust APIs for other language using the `extern` keyword. Private `extern` functions call functions from other languages, while public functions can be called in other languages:
```rust
extern "C" {
	fn abs(input: i32) -> i32;
}

#[no_mangle]
pub extern "C" fn call_from_c() {
	println!("Just called a Rust function from C!");
}
```