30 Days of Rust - Day 30
Tags: rust, devjournal, learning
Published: 2023-07-05 06:00:00

This is it, the final blog. It's long and arduous, so take some time to read it. Don't worry, I'll still be posting blogs, but probably not on the features of the Rust language - I think we all need a break from that for now.

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

## Today's open questions
*No open questions

## Associated types vs generics
We've already looked at how generics are a way to improve reusability of functions or traits in Rust. Associated types offer a different approach to reusability. There may be instances where a certain trait need only be implemented once for a given type. This is when an associate type definition on a trait may be more useful. Consider this example from the Rust book:
```rust
use std::ops::Add;

#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

impl Add for Point {
    type Output = Point;

    fn add(self, other: Point) -> Point {
        Point {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

fn main() {
    assert_eq!(
        Point { x: 1, y: 0 } + Point { x: 2, y: 3 },
        Point { x: 3, y: 3 }
    );
}
```

Note how in the implementation of `Add` we declare the type `Output`. What's we're actually doing is defining the associate type of the `Add` trait. Without this, our code won't compile. The associate type serves as a contract in this function. As per the trait definition, our associated type serves as the return type of our  `add` function. Here's the trait declaration:
```rust
trait Add<Rhs=Self> {
    type Output;

    fn add(self, rhs: Rhs) -> Self::Output;
}
```
 What's interesting about the `Add` trait is that it also has *default* associate type. This is represented by the `Rhs ` alias input argument.

Default types in this context provide flexibility to library authors: often most users of a library will use a trait in one way. This typical usage pattern can be expressed through the default type of a trait. By making this a parameter, however, you can give power users the chance to use the library in a way you may not have foreseen.

## Fully qualified syntax for duplicated method and associated functions
Rust, like Python, provides no limitations on defining multiple methods of the same name on an object. That's probably because no programming language intended on its users to do that (except when overloading methods, of course).

In Rust, however, there is actually a relatively common situation where a `struct` may have multiple methods or associated functions of the same name. When implementing a `trait` from an external library, we may be forced to use a method name that we have already defined.

###  Syntax for method calls
Remember back to when we talked about multithreading, we briefly talked about the `Send` (and `Sync`) trait. Imagine you want code that implements not only `Send` from the standard library, but also from a third party library that instantiates threads in a different way. Suddenly you have two versions of the `send` method.

When calling these methods, we have to use fully qualified syntax. This consists of the `trait` and the `method name`:
```rust
let thread_struct = MyCustomThread::new();

StdLibrarySendTrait::send(&thread_struct);
ExternalLibrarySendTrait::send(&thread_struct);
```

### Syntax for associated functions
As a `struct` method contains a reference to the `struct` as its first argument, when we call the method using fully qualified syntax, Rust knows which implementation block is being called as it knows the type of `self`:
```rust

impl ExternalLibrarySendTrait for MyCustomThread {
	fn send(&self) {
		// Do something
	}
}

impl ExternalLibrarySendTrait for SomeOtherType {
	fn send(&self) {
		// Do something
	}
}

ExternalLibrarySendTrait::send(&thread_struct);
```

This is different for associate functions which don't take `self`:
```rust
trait Animal {
    fn baby_name() -> String;
}

struct Dog;
struct Cat;

impl Dog {
    fn baby_name() -> String {
        String::from("Spot")
    }
}

impl Animal for Dog {
    fn baby_name() -> String {
        String::from("puppy")
    }
}

impl Animal for Cat {
	fn baby_name() -> String {
		String::from("kitten")
	}
}


Dog::baby_name() // No problems
Animal::baby_name() // Cat or Dog?

```

You can see here how the fully qualified syntax for methods isn't enough here (also, the code won't compile). That's actually because we didn't use *fully* fully qualified syntax. Before we could skip the type aliasing because it's implicit in the `&self` argument. Here is how we use fully qualified syntax which will get the above code to compile:
```rust
<Dog as Animal>::baby_name() // A puppy
<Cat as Animal>::baby_name() // A kitten
```

We could use this syntax for methods, too, but it's overly verbose.

## Supertraits: declaring trait dependencies

When defining a `trait` we can declare an optional `supertrait` which must be implemented if that `trait` is to work as expected. Using a `struct` that does not implement the `supertrait` will cause you code to not compile:
```rust
trait PrettyPrint: fmt::Display {
	// Do something
}
```

This code declares a `trait` that can only be implemented on `structs` that implement `Display`.

Supertraits can help reduce code duplication at the cost of increasing code coupling and complexity.


## Type wizardry
Until now we've barely touched on the `type` keyword. This keyword is used to declare type aliases:
```rust
type Kilometer = i32;
```
What's important to understand is that `Kilometer` is still an `i32`:
```rust
let a: Kilometer = 1;
let b = 1;

asserteq!(a + b, 2);
```

Often we see the *newtype* pattern in Rust. This is popular because of Rust's `trait` implementation rule:
> A trait can only be implemented for a type if the trait *or* the type is local to your code.

We can implement the above code using the new type pattern:
```rust
struct Kilometer(i32);

let a = Kilometer(1);
let b = 1;

asserteq!(a + b, 2);
```

This code won't compile, because the `Kilometer` type doesn't implement the `Add` trait. `i32` *does* implement the trait, which is is proof that the `type` alias really is an alias around `i32`.

### When to alias and newtype?
Aliasing makes sense when:
- You have a long type (nested Result or Options, for example) that can be shortened
- A composite type where one part is always the same but the other should be treated as a generic
- Giving a meaningful name to an existing type (like `i32`) can make your code easier to read.

*Newtyping* makes sense when:
- You need to implement external traits
- You want to use a public API to control access to internal code

### The type you never knew about
Functions that return  `!` return the so-called *never* type. The *never* type is a Rust alias for the *empty* type. The point of a never type is that it expresses a function that never *returns*.

Functions that do this yield a result that can be coerced into any other type. This is useful when using `match` statements, as these must always return a single type. Keywords and macros such as `continue`, `panic!` and `loop` return the never type.

### Sized types
A short note on sized types. Generic functions by default can only take *sized* types. A type is *sized* when its size is known at compile time. Sized types by default implement the `Sized` trait.

If you want to use a dynamically sized type with generics you need to use the `?Trait` notation:
```rust
fn generic<T: ?Sized>(t: &T) {}
```
Note that we also expect to receive a reference to the dynamically sized type as these are also stored on the heap behind a pointer.

## Passing and return functions
Functions can easily passed to functions using the `fn` type. This is different from the `trait` restraints used to indicate that a `closure` should be passed as an argument. As something of type `fn` implements all three `Fn` traits, you can always pass functions where closures are accepted.

If you want to return a `closure` from a function, you have to wrap it in a `Box` as a `closure`'s size is determined at runtime.

## Last but not least: Macros
The most advanced Rust feature is the `macro`. These are functions that use Rust code to generate more Rust code. This pattern is generally known as *metaprogramming*.

Rust has different kinds of macros. This have different implementation detail, and each type is suited to a different use case.

### `macro_rules!` macros
The most common type of macro in Rust is the declarative macro. This is created by using the `macro_rules!` macro. In a declarative macro, you can match Rust source code to patterns and generate different kind of code based on which kind of pattern is matched. This makes it possible for these kind of macros to take an undefined number of arguments.


### Procedural macros
The remaining three types of macros are procedural. These macros must take Rust source code as an input and return it.

The first of these is the *derive* macro. To create a derive macro, you need to derive from the `proc_macro_derive` macro of the standard library.  Derive macros **only work** 
on `structs` and `enums`.

Derive macros have a number of restrictions around packaging. When developing such macros, developers are encouraged to include them as a dependency of the main library being developed. Then the macro can simply be reexported as part of the main library for users to import.

Attribute macros are similar to derive macros, only that they are more flexible as they can be used for functions and other data types as well. They derive from the `proc_macro_attribute` macro.

Finally, you have function-like macros. Though procedural in nature, these macros can be used inline like a `macro_rules!` macro and can take an unspecified number of arguments. They derive from `proc_macro`.