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)
|
- [Home](index.md)
|
||||||
- [Résumé](resume.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)
|
- [An Auto-Increment Crate for Rust](an_auto_increment_crate_for_rust.md)
|
||||||
- [Rust Packages vs Crates](rust_packages_vs_crates.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. */
|
/* 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;
|
font-size: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
.left { float: left; }
|
.left {
|
||||||
.right { float: right; }
|
float: left;
|
||||||
.boring { opacity: 0.6; }
|
}
|
||||||
.hide-boring .boring { display: none; }
|
.right {
|
||||||
.hidden { display: none !important; }
|
float: right;
|
||||||
|
}
|
||||||
|
.boring {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
.hide-boring .boring {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
h2, h3 { margin-block-start: 2.5em; }
|
h2,
|
||||||
h4, h5 { margin-block-start: 2em; }
|
h3 {
|
||||||
|
margin-block-start: 2.5em;
|
||||||
|
}
|
||||||
|
h4,
|
||||||
|
h5 {
|
||||||
|
margin-block-start: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
.header + .header h3,
|
.header + .header h3,
|
||||||
.header + .header h4,
|
.header + .header h4,
|
||||||
@ -80,7 +101,9 @@ h6:target::before {
|
|||||||
.page {
|
.page {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
padding: 0 var(--page-padding);
|
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 {
|
.page-wrapper {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -88,10 +111,14 @@ h6:target::before {
|
|||||||
}
|
}
|
||||||
.no-js .page-wrapper,
|
.no-js .page-wrapper,
|
||||||
.js:not(.sidebar-resizing) .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 {
|
[dir="rtl"] .js:not(.sidebar-resizing) .page-wrapper {
|
||||||
transition: margin-right 0.3s ease, transform 0.3s ease; /* Animation: slide away */
|
transition:
|
||||||
|
margin-right 0.3s ease,
|
||||||
|
transform 0.3s ease; /* Animation: slide away */
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
@ -103,12 +130,25 @@ h6:target::before {
|
|||||||
margin-inline-end: auto;
|
margin-inline-end: auto;
|
||||||
max-width: var(--content-max-width);
|
max-width: var(--content-max-width);
|
||||||
}
|
}
|
||||||
.content p { line-height: 1.45em; }
|
.content p {
|
||||||
.content ol { line-height: 1.45em; }
|
line-height: 1.45em;
|
||||||
.content ul { line-height: 1.45em; }
|
}
|
||||||
.content a { text-decoration: none; }
|
.content ol {
|
||||||
.content a:hover { text-decoration: underline; }
|
line-height: 1.45em;
|
||||||
.content img, .content video { max-width: 100%; }
|
}
|
||||||
|
.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:link,
|
||||||
.content .header:visited {
|
.content .header:visited {
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
@ -117,6 +157,14 @@ h6:target::before {
|
|||||||
.content .header:visited:hover {
|
.content .header:visited:hover {
|
||||||
text-decoration: none;
|
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 {
|
table {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
@ -144,14 +192,13 @@ table tbody tr:nth-child(2n) {
|
|||||||
background: var(--table-alternate-bg);
|
background: var(--table-alternate-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
background-color: var(--quote-bg);
|
background-color: var(--quote-bg);
|
||||||
border-block-start: .1em solid var(--quote-border);
|
border-block-start: 0.1em solid var(--quote-border);
|
||||||
border-block-end: .1em solid var(--quote-border);
|
border-block-end: 0.1em solid var(--quote-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning {
|
.warning {
|
||||||
|
@ -166,7 +166,7 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">{{ book_title }}</h1>
|
<h1 class="menu-title"><a href="/">{{ book_title }}</a></h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
{{#if print_enable}}
|
{{#if print_enable}}
|
||||||
@ -215,6 +215,13 @@
|
|||||||
{{{ content }}}
|
{{{ content }}}
|
||||||
</main>
|
</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">
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
<!-- Mobile navigation buttons -->
|
<!-- Mobile navigation buttons -->
|
||||||
{{#previous}}
|
{{#previous}}
|
||||||
@ -232,6 +239,8 @@
|
|||||||
<div style="clear: both"></div>
|
<div style="clear: both"></div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
Loading…
Reference in New Issue
Block a user