Skip to content

Commit

Permalink
Merge branch 'master' into add-links
Browse files Browse the repository at this point in the history
  • Loading branch information
SupperZum authored Dec 16, 2024
2 parents ee2c874 + a479b29 commit e64e34b
Show file tree
Hide file tree
Showing 82 changed files with 369 additions and 397 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
/.vscode/
/*.iml
.DS_Store

Cargo.lock
/target/
4 changes: 4 additions & 0 deletions .markdown-link-check.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"ignorePatterns": [{
"pattern": "^!--"
},{
"pattern": "^https://aayushyavajpayee.substack.com"
}, {
"pattern": "^https://github.com/instrumentisto/rust-incubator/generate"
}, {
"pattern": "^https://crates.io/search\\?"
Expand All @@ -22,6 +24,8 @@
"pattern": "^https://dl.acm.org"
}, {
"pattern": "^https://dpc.pw"
}, {
"pattern": "^https://kerkour.com"
}, {
"pattern": "^https://www.aspecto.io"
}, {
Expand Down
13 changes: 7 additions & 6 deletions 0_vocabulary/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Step 0: Building Up an Vocabulary
Chapter 0: Building Up an Vocabulary
========================================

__Estimated time__: 4 days
Expand All @@ -14,6 +14,8 @@ Read through [Cargo Book] and become familiar with [Cargo] and its workspaces.
After completing these steps, you should be able to answer (and understand why) the following questions:
- What [memory model][31] [Rust] has? Is it single-threaded or multi-threaded? Is it synchronous or asynchronous? What are the memory layouts of `Box` and `Vector`? What are a heap and a stack? Where, but on heap and stack data could live in RAM?
- What runtime [Rust] has? Does it use a GC (garbage collector)?
- What is special about slice? What is the layout of Rust standard data types? Difference between fat and thin pointers?
- Why does [Rust] have `&str` and `String` types? How do they differ? When should you use them? Why str slice coexists with slice? What is the difference between `String` and `Vec`?
- What static typing means? What are the benefits of using it? Weak vs strong typing? Implicit vs explicit typing?
- What are generics and parametric polymorphism? Which problems do they solve?
- What are nominative typing and structural typing? What is the difference?
Expand All @@ -28,16 +30,14 @@ After completing these steps, you should be able to answer (and understand why)
- What is an iterator? What is a collection? How do they differ? How are they used?
- What are macros? Which problems do they solve? What is the difference between declarative and procedural macro?
- How code is tested in [Rust]? Where should you put tests and why?
- What is special about slice? What is the layout of Rust standard data types? Difference between fat and thin pointers?
- Why does [Rust] have `&str` and `String` types? How do they differ? When should you use them? Why str slice coexists with slice? What is the difference between `String` and `Vec`?
- Is [Rust] an OOP language? Is it possible to use SOLID/GRASP? Does it have inheritance? Is Rust a functional language? What variance rules does Rust have?

After you are done, notify your lead in an appropriate PR (pull request), and they will examine what you have learned.

_Additional_ articles, which may help to understand the above topic better:
- [Chris Morgan: Rust ownership, the hard way][1]
- [Adolfo Ochagavía: You are holding it wrong][23]
- [Vikram Fugro: Beyond Pointers: How Rust outshines C++ with its Borrow Checker][30]
- [Vikram Fugro: Beyond Pointers: How Rust outshines C++ with its Borrow Checker][29]
- [HashRust: A guide to closures in Rust][24]
- [Ludwig Stecher: Rusts Module System Explained][2]
- [Tristan Hume: Models of Generics and Metaprogramming: Go, Rust, Swift, D and More][3]
Expand All @@ -58,7 +58,7 @@ _Additional_ articles, which may help to understand the above topic better:
- [Auto-trait definition][18]
- [Georgios Antonopoulos: Rust vs Common C++ Bugs][21]
- [Yurii Shymon: True Observer Pattern with Unsubscribe mechanism using Rust][22]
- [Asynchronous vs Multithreading][29]


Additional:
- [Rust API guidline checklist][19]
Expand Down Expand Up @@ -86,7 +86,7 @@ Additional:
[9]: https://www.thecodedmessage.com/posts/raii
[10]: https://vojtechkral.github.io/blag/rust-drop-order/
[11]: https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md
[12]: https://www.youtube.com/watch?v=rDoqT-a6UFg
[12]: https://youtu.be/7_o-YRxf_cc?si=gSVQ6wWSnr2le6rc
[13]: https://www.reddit.com/r/rust/comments/lvtzri/confused_about_package_vs_crate_terminology/
[14]: https://rustwiki.org/en/book/ch07-01-packages-and-crates.html
[16]: https://github.com/integer32llc/rust-playground/blob/master/compiler/base/Cargo.toml
Expand All @@ -105,3 +105,4 @@ Additional:
[29]: https://github.com/Learn-Together-Pro/ComputerScience/blob/master/gcs/cheatsheets.md#asynchronous-vs-multithreading
[30]: https://dev.to/vikram2784/beyond-pointers-how-rust-outshines-c-with-its-borrow-checker-1mad
[31]: https://en.wikipedia.org/wiki/Memory_model_(programming)

2 changes: 1 addition & 1 deletion 1_concepts/1_1_default_clone_copy/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "step_1_1"
name = "task_1_1"
version = "0.1.0"
edition = "2021"
publish = false
2 changes: 1 addition & 1 deletion 1_concepts/1_1_default_clone_copy/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Step 1.1: Default values, cloning and copying
Task 1.1: Default values, cloning and copying
=============================================


Expand Down
2 changes: 1 addition & 1 deletion 1_concepts/1_2_box_pin/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "step_1_2"
name = "task_1_2"
version = "0.1.0"
edition = "2021"
publish = false
7 changes: 4 additions & 3 deletions 1_concepts/1_2_box_pin/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Step 1.2: Boxing and pinning
Task 1.2: Boxing and pinning
============================


Expand Down Expand Up @@ -42,6 +42,7 @@ For better understanding [`Pin`] purpose, design, limitations and use cases read
- [Alice Ryhl answers on "Pin tutorial are confusing me"][9]
- [Rust Forum: Why is it unsafe to pin a shared reference?][11]
- [Ohad Ravid: Put a Pin on That][12]
- [Razieh Behjati: Leaky Abstractions and a Rusty Pin][13]



Expand All @@ -59,10 +60,9 @@ __Estimated time__: 1 day
#### Important:
##### THERE HAS TO BE NO `unsafe` CODE (DON'T USE `unsafe`)
> - `mut_me_somehow` must mutate self somehow.
> - You can add trait bounds to the types.
> - You can add trait bounds to the types. (using `Unpin` trait bound is not allowed)
> - Write simple tests to demonstrate mut_me_somehow.
> - you may use modules to avoid conflicting implementations
```rust
trait SayHi: fmt::Debug {
Expand Down Expand Up @@ -132,3 +132,4 @@ After completing everything above, you should be able to answer (and understand
[10]: https://www.sobyte.net/post/2022-07/rust-pin-unpin
[11]: https://users.rust-lang.org/t/why-is-it-unsafe-to-pin-a-shared-reference/40309
[12]: https://ohadravid.github.io/posts/2023-07-put-a-pin-on-that
[13]: https://itnext.io/leaky-abstractions-and-a-rusty-pin-fbf3b84eea1f
2 changes: 1 addition & 1 deletion 1_concepts/1_3_rc_cell/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "step_1_3"
name = "task_1_3"
version = "0.1.0"
edition = "2021"
publish = false
2 changes: 1 addition & 1 deletion 1_concepts/1_3_rc_cell/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Step 1.3: Shared ownership and interior mutability
Task 1.3: Shared ownership and interior mutability
==================================================


Expand Down
2 changes: 1 addition & 1 deletion 1_concepts/1_4_cow/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "step_1_4"
name = "task_1_4"
version = "0.1.0"
edition = "2021"
publish = false
2 changes: 1 addition & 1 deletion 1_concepts/1_4_cow/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Step 1.4: Clone-on-write
Task 1.4: Clone-on-write
========================


Expand Down
2 changes: 1 addition & 1 deletion 1_concepts/1_5_convert_cast_deref/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "step_1_5"
name = "task_1_5"
version = "0.1.0"
edition = "2021"
publish = false
2 changes: 1 addition & 1 deletion 1_concepts/1_5_convert_cast_deref/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Step 1.5: Conversions, casting and dereferencing
Task 1.5: Conversions, casting and dereferencing
================================================

As [Rust] is a [strongly typed][1] language, all type conversions must be performed explicitly in the code. As [Rust] has a rich type system (programming logic and semantics are mostly expressed in types rather than in values), type conversions are inevitable in almost every single line of code. Fortunately, [Rust] offers [well-designed type conversion capabilities][`std::convert`], which are quite ergonomic, intuitive and are pleasant to use.
Expand Down
2 changes: 1 addition & 1 deletion 1_concepts/1_6_dispatch/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "step_1_6"
name = "task_1_6"
version = "0.1.0"
edition = "2021"
publish = false
8 changes: 5 additions & 3 deletions 1_concepts/1_6_dispatch/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
Step 1.6: Static and dynamic dispatch
Task 1.6: Static and dynamic dispatch
=====================================

[Static][1] and [dynamic][2] dispatches are important concepts to understand how your code is compiled and works in runtime, and how you can solve certain day-to-day coding problems (related to polymorphism).

__[Static dispatch][1]__ (also called "early binding") __happens only at compile time__. The compiler generates separate code for each concrete type that is used. In [Rust] static dispatch is a __default way for polymorphism__ and is introduced simply by generics (parametric polymorphism): `MyType<T, S, F>`.

__[Dynamic dispatch][2]__ (sometimes called "late binding") __happens at runtime__. The concrete used __[type is erased][4] at compile time__, so compiler doesn't know it, therefore generates `vtable` which dispatches call at runtime and __comes with a performance penalty__. In [Rust] dynamic dispatch is introduced via [trait objects][3]: `&dyn MyTrait`, `Box<dyn MyTrait>`.
__[Dynamic dispatch][2]__ (sometimes called "late binding") __happens at runtime__. The concrete used __[type is erased][4] at compile time__, so compiler doesn't know it, therefore generates [vtable] which dispatches call at runtime and __comes with a performance penalty__. In [Rust] dynamic dispatch is introduced via [trait objects][3]: `&dyn MyTrait`, `Box<dyn MyTrait>`.

You _have to_ use [dynamic dispatch][2] in situations where [type erasure][4] is required. If the problem can be solved with a [static dispatch][1] then you'd better to do so to avoid performance penalties. The most common example when you cannot use [static dispatch][1] and have to go with [dynamic dispatch][2] are _heterogeneous_ collections (where each item is potentially a different concrete type, but each one implements `MyTrait`).

Expand All @@ -18,7 +18,7 @@ For better understanding [static][1] and [dynamic][2] dispatches purpose, design
- [Nicholas Matsakis: Dyn async traits, part 2][17]
- [Armin Ronacher: Rust Any Part 1: Extension Maps in Rust][18]
- [Armin Ronacher: Rust Any Part 2: As-Any Hack][19]

- [Dynamic Dispatch Representation][21]



Expand Down Expand Up @@ -187,6 +187,7 @@ After completing everything above, you should be able to answer (and understand
[enum_dispatch]: https://docs.rs/enum_dispatch
[momo]: https://github.com/llogiq/momo
[Rust]: https://www.rust-lang.org
[vtable]: https://en.wikipedia.org/wiki/Virtual_method_table

[1]: https://en.wikipedia.org/wiki/Static_dispatch
[2]: https://en.wikipedia.org/wiki/Dynamic_dispatch
Expand All @@ -204,3 +205,4 @@ After completing everything above, you should be able to answer (and understand
[18]: https://lucumr.pocoo.org/2022/1/6/rust-extension-map
[19]: https://lucumr.pocoo.org/2022/1/7/as-any-hack
[20]: https://medium.com/digitalfrontiers/rust-dynamic-dispatching-deep-dive-236a5896e49b
[21]: https://www.cs.brandeis.edu/~cs146a/rust/doc-02-21-2015/book/static-and-dynamic-dispatch.html#representation
2 changes: 1 addition & 1 deletion 1_concepts/1_7_sized/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "step_1_7"
name = "task_1_7"
version = "0.1.0"
edition = "2021"
publish = false
2 changes: 1 addition & 1 deletion 1_concepts/1_7_sized/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Step 1.7: `Sized` and `?Sized` types
Task 1.7: `Sized` and `?Sized` types
====================================

Most types in [Rust] have a particular size, in bytes, that is knowable at compile time. For example, an `i32` is 32 bits big, or 4 bytes. However, there are some types which are useful to express, but do not have a defined size (called "unsized" or "dynamically sized" types). One example is `[T]`: it represents a certain number of `T` in a sequence, but we don’t know how many there are, so the size is not known.
Expand Down
2 changes: 1 addition & 1 deletion 1_concepts/1_8_thread_safety/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "step_1_8"
name = "task_1_8"
version = "0.1.0"
edition = "2021"
publish = false
4 changes: 3 additions & 1 deletion 1_concepts/1_8_thread_safety/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Step 1.8: Thread safety
Task 1.8: Thread safety
=======================

[Rust] has [`Send`] and [`Sync`] marker traits which are fundamental for concurrency and thread safety story in [Rust] and represent one of [fearless concurrency][2] corner stones (which allow to [avoid data races][1] at compile time).
Expand All @@ -13,6 +13,7 @@ For better understanding [`Send`]/[`Sync`] purpose, design, limitations and use
- [nyanpasu64: An unsafe tour of Rust's Send and Sync][6]
- [Josh Haberman: Thread Safety in C++ and Rust][7]
- [Cliff L. Biffle: Safely writing code that isn't thread-safe][8]
- [Louis Dureuil: Too dangerous for C++][10]



Expand Down Expand Up @@ -59,3 +60,4 @@ After completing everything above, you should be able to answer (and understand
[7]: https://blog.reverberate.org/2021/12/18/thread-safety-cpp-rust.html
[8]: https://cliffle.com/blog/not-thread-safe
[9]: https://web.archive.org/web/20220929143451/https://itsallaboutthebit.com/arc-mutex
[10]: https://blog.dureuill.net/articles/too-dangerous-cpp
2 changes: 1 addition & 1 deletion 1_concepts/1_9_phantom/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "step_1_9"
name = "task_1_9"
version = "0.1.0"
edition = "2021"
publish = false
4 changes: 3 additions & 1 deletion 1_concepts/1_9_phantom/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Step 1.9: Phantom types
Task 1.9: Phantom types
=======================

Because [Rust] has a rich type system, a programming logic and semantics are mostly expressed in types rather than in data/values, which is known as a "programming with types" concept. Often, this leads to situations where you need to express some type relations without having any values of those types. Here is where [phantom types][5] come in: they carry some semantics on type level, which invariants are checked by compiler, and are totally compiled out in runtime.
Expand Down Expand Up @@ -89,6 +89,7 @@ For better understanding [`PhantomData`] purpose, design, limitations and use ca
- [Rustonomicon: 3.10. PhantomData][2]
- [Reddit: Why PhantomData][3]
- [RIP Tutorial: Using PhantomData as a Type Marker][4]
- [Aayushya Vajpayee: Write Cleaner, More Maintainable Rust Code with PhantomData][11]
- [Sergey Potapov: Phantom Types in Rust][6]


Expand Down Expand Up @@ -208,3 +209,4 @@ After completing everything above, you should be able to answer (and understand
[8]: https://docs.rs/variance/0.1.3/src/variance/lib.rs.html#16
[9]: https://docs.rs/variance/0.1.3/src/variance/lib.rs.html#92
[10]: https://manishearth.github.io/blog/2017/01/11/rust-tidbits-what-is-a-lang-item
[11]: https://aayushyavajpayee.substack.com/p/coming-soon
2 changes: 1 addition & 1 deletion 1_concepts/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "step_1"
name = "task_1"
version = "0.1.0"
edition = "2021"
publish = false
6 changes: 3 additions & 3 deletions 1_concepts/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
Step 1: Concepts
Chapter 1: Concepts
================

These steps describe common and necessary-to-know concepts for everyday programming in [Rust].
These tasks describe common and necessary-to-know concepts for everyday programming in [Rust].

> ❗️Before completing this step you should complete all its sub-steps.
> ❗️Before completing this task you should complete all its sub-tasks.
After doing them you should be able to answer the following questions:
- How do I recognize that data is allocated at the heap rather than at the stack? When data should be allocated at the heap?
Expand Down
2 changes: 1 addition & 1 deletion 2_idioms/2_1_type_safety/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "step_2_1"
name = "task_2_1"
version = "0.1.0"
edition = "2021"
publish = false
2 changes: 1 addition & 1 deletion 2_idioms/2_1_type_safety/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Step 2.1: Rich types ensure correctness
Task 2.1: Rich types ensure correctness
=======================================

[Rust] has a rich type system which allows to express our program primitives, entities, notions, logic and semantics mostly in types, rather than in data/values, which is known as a "programming with types" concept. The benefits of this are obvious: the more compiler knows about our problem - the more false programs it will decline. Or, rephrased: __the more we describe about the program in types - the more we reduce the probability for the program to be incorrect__.
Expand Down
2 changes: 1 addition & 1 deletion 2_idioms/2_2_mem_replace/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "step_2_2"
name = "task_2_2"
version = "0.1.0"
edition = "2021"
publish = false
6 changes: 4 additions & 2 deletions 2_idioms/2_2_mem_replace/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Step 2.2: Swapping values with `mem::replace`
Task 2.2: Swapping values with `mem::replace`
=============================================

As [Rust] implies [move semantics][1] by default and quite strict [borrowing rules][2], often, there are situations (especially, with large `struct`s and `enum`s) where mutating value in-place or values swapping may not be allowed by borrow checker, which is quite confusing and leads to doing needless clones (so providing redudant performance costs). For example:
Expand Down Expand Up @@ -27,6 +27,7 @@ For better understanding [`mem::replace`], [`mem::swap`] and [`mem::take`] purpo
- [Official `mem::swap` docs][`mem::swap`]
- [Official `mem::take` docs][`mem::take`]
- [Karol Kuczmarski: Moving out of a container in Rust][4]
- [Ferrous Systems: Using `mem::take` to reduce heap allocations][6]

Some examples of useful applying these functions are described below.

Expand Down Expand Up @@ -141,7 +142,7 @@ __Estimated time__: 1 day



Improve and optimize the code contained in [this step's crate](src/main.rs) to cut off redudant performance costs.
Improve and optimize the code contained in [this task's crate](src/main.rs) to cut off redudant performance costs.
Add tests.


Expand All @@ -165,3 +166,4 @@ After completing everything above, you should be able to answer (and understand
[3]: https://rust-unofficial.github.io/patterns/idioms/mem-replace.html
[4]: http://xion.io/post/code/rust-move-out-of-container.html
[5]: https://doc.rust-lang.org/edition-guide/rust-2021/disjoint-capture-in-closures.html
[6]: https://ferrous-systems.com/blog/rustls-borrow-checker-p1
2 changes: 1 addition & 1 deletion 2_idioms/2_3_bound_impl/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "step_2_3"
name = "task_2_3"
version = "0.1.0"
edition = "2021"
publish = false
4 changes: 2 additions & 2 deletions 2_idioms/2_3_bound_impl/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Step 2.3: Bound behavior, not data
Task 2.3: Bound behavior, not data
==================================

Often, when we want to abstract over some type or behavior in [Rust] we are going from this:
Expand Down Expand Up @@ -106,7 +106,7 @@ __Estimated time__: 1 day



Refactor the code contained in [this step's crate](src/main.rs) to reduce trait bounds pollution as much as possible.
Refactor the code contained in [this task's crate](src/main.rs) to reduce trait bounds pollution as much as possible.



Expand Down
2 changes: 1 addition & 1 deletion 2_idioms/2_4_generic_in_type_out/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "step_2_4"
name = "task_2_4"
version = "0.1.0"
edition = "2021"
publish = false
6 changes: 3 additions & 3 deletions 2_idioms/2_4_generic_in_type_out/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Step 2.4: Abstract type in, concrete type out
Task 2.4: Abstract type in, concrete type out
=============================================


Expand Down Expand Up @@ -74,7 +74,7 @@ just_print_stringy(&nickname);

This is one of the key features, which drive [Rust] expressiveness and ergonomics. Just look over `std` library to see how widely it's used: [`Iterator::eq()`][1], [`Vec::drain()`][2], [`HashMap::extend()`][3], etc.

The downside of this idiom is that compiler generates more code due to monomorphization, so potentially leads to code bloating. The way it can be optimized has already been [explained in "Reducing code bloat optimization" section of 1.6 step][6].
The downside of this idiom is that compiler generates more code due to monomorphization, so potentially leads to code bloating. The way it can be optimized has already been [explained in "Reducing code bloat optimization" section of 1.6 task][6].

Further reading on theme:
- [Joe Wilm: From &str to Cow][4]
Expand Down Expand Up @@ -102,7 +102,7 @@ __Estimated time__: 1 day



Refactor the code contained in [this step's crate](src/main.rs) to make it more efficient, idiomatic, simple and pleasant to use.
Refactor the code contained in [this task's crate](src/main.rs) to make it more efficient, idiomatic, simple and pleasant to use.



Expand Down
Loading

0 comments on commit e64e34b

Please sign in to comment.