Add rust guide part 1; Add CCBY license to pages
This commit is contained in:
parent
31956191bd
commit
f06eb61b9e
@ -2,6 +2,9 @@
|
||||
|
||||
- [Home](index.md)
|
||||
- [Résumé](resume.md)
|
||||
- [Rust](rust.md)
|
||||
|
||||
# Rust Stuff
|
||||
|
||||
- [Generics Guide Part 1](rust-generics-guide-part-1.md)
|
||||
- [An Auto-Increment Crate for Rust](an_auto_increment_crate_for_rust.md)
|
||||
- [Rust Packages vs Crates](rust_packages_vs_crates.md)
|
||||
|
207
src/rust-generics-guide-part-1.md
Normal file
207
src/rust-generics-guide-part-1.md
Normal file
@ -0,0 +1,207 @@
|
||||
# Rust Generics Guide Part 1
|
||||
|
||||
<!-- TOC -->
|
||||
- [Introduction](#introduction)
|
||||
- [The Goal](#the-goal)
|
||||
- [The Basics](#the-basics)
|
||||
- [Type Definitions](#type-definitions)
|
||||
- [Functions and Closures](#functions-and-closures)
|
||||
- [Conclusion](#conclusion)
|
||||
<!-- /TOC -->
|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||
Rustaceans appreciate generics for three reasons:
|
||||
|
||||
- Generics are **compile-time abstractions**. The `dyn` keyword, which generics can often replace,
|
||||
has runtime overhead.
|
||||
- Generics make code cleaner and more reusable. Thanks to trait bounds, code can describe *what* it
|
||||
can do *when* those bounds are met. **Your code becomes exactly as flexible as you want it to
|
||||
be.**
|
||||
- Generics will help you understand lifetimes. [The Book][TheBook] explains lifetimes with *imperative*
|
||||
code (i.e. code in a function body). But **generic lifetimes have *declarative* context**. This
|
||||
gives them clear meaning when used in types, traits and implementations.
|
||||
|
||||
Let's learn everything there is to know about generics. We'll do this by learning incrementally so
|
||||
that each piece of the lesson is digested before we go back for more.
|
||||
|
||||
Scroll to the bottom for a runnable example of the code we'll be writing.
|
||||
|
||||
## The Goal
|
||||
|
||||
Walking through a few simple examples can prepare anyone who has worked through [The
|
||||
Book][TheBook] to use generics with confidence. Generics allow us to code with **selective
|
||||
flexibility**. Once the tools are understood, the concept of plugging in types where appropriate
|
||||
will come naturally.
|
||||
|
||||
Starting with a dead-simple example, we'll do exactly what generic (or *parametized*) types are
|
||||
supposed to do by introducing **type flexibility**. Then we can gently layer on the complexity and
|
||||
learn why generics permeate into every aspect of the type system, why they need **optional
|
||||
restrictions** and how we can use this powerful tooling without turning our code into cryptic goop.
|
||||
By the end, we will have full control of Rust's type system.
|
||||
|
||||
This guide will walk you through the process of writing a small library with plenty of code
|
||||
examples. Each part can be completed in less than thirty minutes. If you follow along, the code
|
||||
will compile after each section. Part one will cover the basics of types, functions and closures.
|
||||
Part two will dig into implementations and traits. Finally, part three will add `const` generics
|
||||
and parametized lifetimes. The resulting library will be simple but fun and easy to understand.
|
||||
|
||||
**Part one mostly reviews information covered in The Book, but with some heplful observations.** If
|
||||
you have questions, criticism or feedback, you can find contact information on the [homepage](/).
|
||||
|
||||
## The Basics
|
||||
|
||||
### Type Definitions
|
||||
|
||||
Before getting into elaborate implementations that use multiple generics at every possible level,
|
||||
let's focus on brass tacks.
|
||||
|
||||
```rust,noplayground
|
||||
struct Point {
|
||||
id: u64
|
||||
}
|
||||
```
|
||||
|
||||
This is familiar territory, `u64` is a hard-coded (non-generic) type. It offers no flexibility but
|
||||
is simple and selective.
|
||||
|
||||
```rust,noplayground
|
||||
struct Point<Id> {
|
||||
id: Id
|
||||
}
|
||||
```
|
||||
|
||||
This `Point` can use *any* other type as the `Id`. The `Id` could even be `Point` or a vector of
|
||||
`Point`s, which would look pretty absurd: `Point<Vec<Point<()>>`.
|
||||
|
||||
This flexibility can be excessive, which is how the `where` clause will help later on when we use an
|
||||
implementation. For now, we can keep the `Point<Id>` type private from the user while we expose
|
||||
public types with an `Id` that makes sense. We *could* use a trait bound in this type definition.
|
||||
But you will rarely see this done. The reasons for that will be covered in detail in part two when
|
||||
we write implementations and traits of our own.
|
||||
|
||||
```rust,noplayground
|
||||
pub type Point = inner::Point<u32>;
|
||||
pub type BigPoint = inner::Point<u128>;
|
||||
|
||||
mod inner {
|
||||
struct Point<Id> {
|
||||
id: Id
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the `Id` value is going to be randomly generated, larger types like `u128` will allow for lower
|
||||
chances of collision when generating `BigPoint` instances in huge quantities. But for fewer
|
||||
`Point`s, the `u32` type will use just four bytes instead of the sixteen bytes used by each `u128`.
|
||||
This is a simplified example of how generics are useful when defining types.
|
||||
|
||||
We have now seen three levels of flexibility:
|
||||
|
||||
- A hard-coded (non-generic) type.
|
||||
- A highly flexible open-ended generic type.
|
||||
- A generic type used privately with certain types plugged in for the public interface.
|
||||
|
||||
### Functions and Closures
|
||||
|
||||
A function consists of a type definition (the function signature) and an execution context (the
|
||||
imperative code block). It is helpful to start with a simple function before moving on to
|
||||
implementations.
|
||||
|
||||
```rust,noplayground
|
||||
struct Line<P> {
|
||||
points: (P, P),
|
||||
}
|
||||
|
||||
fn connect<P>(left: P, right: P) -> Line<P> {
|
||||
Line {
|
||||
points: (left, right)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here the type used in the `points` tuple is decided **wherever `connect` is called, because that is
|
||||
where `Line` is invoked**. If tight coupling is acceptable, we can specify that `connect` always
|
||||
deals with `Line`s and `Point`s, while still allowing a generic type in the `Point`'s `id` field.
|
||||
|
||||
```rust,noplayground
|
||||
fn connect<Id>(left: Point<Id>, right: Point<Id>) -> Line<Point<Id>> {
|
||||
Line {
|
||||
points: (left, right)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We still have a type parameter, but now the `Point` and `Line` types are tightly coupled in this
|
||||
function.
|
||||
|
||||
As with any function, the arguments could be written as a `struct` and the function body could be
|
||||
moved to a method. Instead of weighing in on the eternal struggle of functional vs object-oriented
|
||||
code aesthetics, we will simply observe that function signatures are type definitions. This is not
|
||||
a trivial observation. For now, it's enough to simply point it out. Let's move on to using a
|
||||
generic type for a closure.
|
||||
|
||||
```rust
|
||||
/// This is the same Point we used to first demonstrate a generic type.
|
||||
#[derive(Debug)]
|
||||
struct Point<Id> {
|
||||
id: Id
|
||||
}
|
||||
|
||||
/// This Line is different. We've restricted it to use the Point type but left
|
||||
/// the Id flexible. Before, this tight coupling was done in the `connect`
|
||||
// method.
|
||||
#[derive(Debug)]
|
||||
struct Line<Id> {
|
||||
points: (Point<Id>, Point<Id>),
|
||||
}
|
||||
|
||||
/// This new type contains a closure. While Box is a type, this involves Fn, a
|
||||
/// trait that uses generic types. We will write our own such trait later.
|
||||
struct Connector<Id> {
|
||||
pub op: Box<dyn Fn(Point<Id>, Point<Id>) -> Line<Id>>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let connector: Connector<String> = Connector {
|
||||
op: Box::new(|left, right| Line {
|
||||
points: (left, right),
|
||||
}),
|
||||
};
|
||||
|
||||
let point_a = Point {
|
||||
id: "A".to_string(),
|
||||
};
|
||||
let point_b = Point {
|
||||
id: "B".to_string(),
|
||||
};
|
||||
let line = (connector.op)(point_a, point_b);
|
||||
|
||||
println!("{:?}", line);
|
||||
}
|
||||
```
|
||||
|
||||
Here we have built a tiny library that, with just a little more work, might do something
|
||||
interesting. If we iterate over pairs of `Point`s we could allow the user to connect them while
|
||||
also recording, modifying or filtering them. Meanwhile, our library could handle how the iterating
|
||||
works. Or instead of iterating over pairs and returning `Lines`, we could select them in groups of
|
||||
whatever size the user wants and combine them into types that we provide. We could also let the
|
||||
user write their own `Line`, `Square`, `Graph` or whatever they want by having them plug it into a
|
||||
*type parameter* (i.e. generic type).
|
||||
|
||||
It is with this cliffhanger that we'll stop with part one. The good stuff comes next.
|
||||
|
||||
## Conclusion
|
||||
|
||||
It might seem that we have all we need with the tools we've demonstrated so far. It's true that
|
||||
**you can do a lot with these few simple tools**. But Rust needs more syntax in order to feature
|
||||
the power of generics in all their glory. While part two will be more complex than this lesson, the
|
||||
language as a whole will become easier to understand.
|
||||
|
||||
If you have any questions or comments about this guide, contact information is on the
|
||||
[homepage](../). I appreciate any and all feedback.
|
||||
|
||||
[Go to part two.](../rust_guide_generics_demystified_part_2)
|
||||
|
||||
[TheBook]:https://doc.rust-lang.org/stable/book/title-page.html
|
41
src/rust.md
41
src/rust.md
@ -1,41 +0,0 @@
|
||||
# Rust
|
||||
|
||||
Rust is a systems programming language that I've been using in my projects since 2020. Here's an example from The Rust Programming Language (a.k.a. The Book).
|
||||
|
||||
```rust,noplayground
|
||||
use rand::Rng;
|
||||
use std::cmp::Ordering;
|
||||
use std::io;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = rand::thread_rng().gen_range(1..=100);
|
||||
|
||||
loop {
|
||||
println!("Please input your guess.");
|
||||
|
||||
let mut guess = String::new();
|
||||
|
||||
io::stdin()
|
||||
.read_line(&mut guess)
|
||||
.expect("Failed to read line");
|
||||
|
||||
let guess: u32 = match guess.trim().parse() {
|
||||
Ok(num) => num,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
println!("You guessed: {guess}");
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => {
|
||||
println!("You win!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
@ -37,18 +37,39 @@ main {
|
||||
}
|
||||
|
||||
/* Don't change font size in headers. */
|
||||
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
|
||||
h1 code,
|
||||
h2 code,
|
||||
h3 code,
|
||||
h4 code,
|
||||
h5 code,
|
||||
h6 code {
|
||||
font-size: unset;
|
||||
}
|
||||
|
||||
.left { float: left; }
|
||||
.right { float: right; }
|
||||
.boring { opacity: 0.6; }
|
||||
.hide-boring .boring { display: none; }
|
||||
.hidden { display: none !important; }
|
||||
.left {
|
||||
float: left;
|
||||
}
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
.boring {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.hide-boring .boring {
|
||||
display: none;
|
||||
}
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
h2, h3 { margin-block-start: 2.5em; }
|
||||
h4, h5 { margin-block-start: 2em; }
|
||||
h2,
|
||||
h3 {
|
||||
margin-block-start: 2.5em;
|
||||
}
|
||||
h4,
|
||||
h5 {
|
||||
margin-block-start: 2em;
|
||||
}
|
||||
|
||||
.header + .header h3,
|
||||
.header + .header h4,
|
||||
@ -80,7 +101,9 @@ h6:target::before {
|
||||
.page {
|
||||
outline: 0;
|
||||
padding: 0 var(--page-padding);
|
||||
margin-block-start: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */
|
||||
margin-block-start: calc(
|
||||
0px - var(--menu-bar-height)
|
||||
); /* Compensate for the #menu-bar-hover-placeholder */
|
||||
}
|
||||
.page-wrapper {
|
||||
box-sizing: border-box;
|
||||
@ -88,10 +111,14 @@ h6:target::before {
|
||||
}
|
||||
.no-js .page-wrapper,
|
||||
.js:not(.sidebar-resizing) .page-wrapper {
|
||||
transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */
|
||||
transition:
|
||||
margin-left 0.3s ease,
|
||||
transform 0.3s ease; /* Animation: slide away */
|
||||
}
|
||||
[dir=rtl] .js:not(.sidebar-resizing) .page-wrapper {
|
||||
transition: margin-right 0.3s ease, transform 0.3s ease; /* Animation: slide away */
|
||||
[dir="rtl"] .js:not(.sidebar-resizing) .page-wrapper {
|
||||
transition:
|
||||
margin-right 0.3s ease,
|
||||
transform 0.3s ease; /* Animation: slide away */
|
||||
}
|
||||
|
||||
.content {
|
||||
@ -103,12 +130,25 @@ h6:target::before {
|
||||
margin-inline-end: auto;
|
||||
max-width: var(--content-max-width);
|
||||
}
|
||||
.content p { line-height: 1.45em; }
|
||||
.content ol { line-height: 1.45em; }
|
||||
.content ul { line-height: 1.45em; }
|
||||
.content a { text-decoration: none; }
|
||||
.content a:hover { text-decoration: underline; }
|
||||
.content img, .content video { max-width: 100%; }
|
||||
.content p {
|
||||
line-height: 1.45em;
|
||||
}
|
||||
.content ol {
|
||||
line-height: 1.45em;
|
||||
}
|
||||
.content ul {
|
||||
line-height: 1.45em;
|
||||
}
|
||||
.content a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.content img,
|
||||
.content video {
|
||||
max-width: 100%;
|
||||
}
|
||||
.content .header:link,
|
||||
.content .header:visited {
|
||||
color: var(--fg);
|
||||
@ -117,6 +157,14 @@ h6:target::before {
|
||||
.content .header:visited:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.content hr {
|
||||
max-width: var(--content-max-width);
|
||||
}
|
||||
.content footer {
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
max-width: var(--content-max-width);
|
||||
}
|
||||
|
||||
table {
|
||||
margin: 0 auto;
|
||||
@ -144,14 +192,13 @@ table tbody tr:nth-child(2n) {
|
||||
background: var(--table-alternate-bg);
|
||||
}
|
||||
|
||||
|
||||
blockquote {
|
||||
margin: 20px 0;
|
||||
padding: 0 20px;
|
||||
color: var(--fg);
|
||||
background-color: var(--quote-bg);
|
||||
border-block-start: .1em solid var(--quote-border);
|
||||
border-block-end: .1em solid var(--quote-border);
|
||||
border-block-start: 0.1em solid var(--quote-border);
|
||||
border-block-end: 0.1em solid var(--quote-border);
|
||||
}
|
||||
|
||||
.warning {
|
||||
|
@ -166,7 +166,7 @@
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">{{ book_title }}</h1>
|
||||
<h1 class="menu-title"><a href="/">{{ book_title }}</a></h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
{{#if print_enable}}
|
||||
@ -215,6 +215,13 @@
|
||||
{{{ content }}}
|
||||
</main>
|
||||
|
||||
<hr/>
|
||||
|
||||
<footer>
|
||||
<!-- License from https://creativecommons.org/ -->
|
||||
<p>Licensed under <a href="https://creativecommons.org/licenses/by/4.0?ref=chooser-v1">CC BY 4.0</a></p>
|
||||
</footer>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
{{#previous}}
|
||||
@ -232,6 +239,8 @@
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
|
Loading…
Reference in New Issue
Block a user