diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 4caccd92..dedf9028 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -429,6 +429,14 @@ --- +# Idiomatic Rust + +- [Welcome](idiomatic/welcome.md) +- [Leveraging the Type System](idiomatic/leveraging-the-type-system.md) + - [Newtype Pattern](idiomatic/leveraging-the-type-system/newtype-pattern.md) + +--- + # Final Words - [Thanks!](thanks.md) diff --git a/src/idiomatic/leveraging-the-type-system.md b/src/idiomatic/leveraging-the-type-system.md new file mode 100644 index 00000000..21e1fae5 --- /dev/null +++ b/src/idiomatic/leveraging-the-type-system.md @@ -0,0 +1,47 @@ +--- +minutes: 5 +--- + +# Leveraging the Type System + +Rust's type system is _expressive_.\ +We can use types and traits to build abstractions that make our code harder to +misuse. In some cases, we can even go as far as enforcing correctness at +_compile-time_. Quite often, these abstractions have no runtime +overhead[^zero-cost]. + +The type system can also be used to model concepts and constraints from your +business domain. By designing our types carefully, we can improve the clarity +and maintainability of the entire codebase. + +
+ +Additional items speaker may mention: + +- Rust's type system borrows a lot of ideas from functional programming + languages.\ + For example, Rust's enums are known as "algebraic data types" in languages + like Haskell and OCaml. You can take inspiration from learning material geared + towards functional languages when looking for guidance on how to design with + types. ["Domain Modeling Made Functional"][1] is a great resource on the + topic, with examples written in F#. + +- Despite its functional roots, functional design patterns don't translate as-is + to Rust. For instance, extensive use of higher-kinded functions and types can + result in code that is harder to read and maintain. Design patterns in Rust + must take into account (and leverage!) the granular control over mutability + that comes with its borrow-checker. + +- The same caution should be applied to object-oriented design patterns. Rust + doesn't support inheritance, and object boundaries must be mindful of the + constraints introduced by the borrow-checker. + +
+ +{{%segment outline}} + +[1]: https://pragprog.com/titles/swdddf/domain-modeling-made-functional/ + +[^zero-cost]: They often referred to as "zero-cost abstractions", although the + label can be misleading: the impact on compile times and code complexity may + be significant. diff --git a/src/idiomatic/leveraging-the-type-system/newtype-pattern.md b/src/idiomatic/leveraging-the-type-system/newtype-pattern.md new file mode 100644 index 00000000..e6c04f58 --- /dev/null +++ b/src/idiomatic/leveraging-the-type-system/newtype-pattern.md @@ -0,0 +1,49 @@ +--- +minutes: 5 +--- + +# Newtype Pattern + +A _newtype_ is a wrapper around an existing type, often a primitive: + +```rust +/// A unique user identifier, implemented as a newtype around `u64`. +pub struct UserId(u64); +``` + +Unlike type aliases, newtypes aren't interchangeable with the wrapped type: + +```rust,compile_fail +# pub struct UserId(u64); +fn double(n: u64) -> u64 { + n * 2 +} + +// This doesn't compile ❌ +double(UserId(1)); +``` + +The Rust compiler won't implicitly convert to (or from) the underlying type.\ +It won't let you use methods or operators defined on the underlying type either: + +```rust,compile_fail +# pub struct UserId(u64); +// This doesn't compile ❌ +assert_ne!(UserId(1), UserId(2)); +``` + +
+ +- Run the example to show students the error message from the compiler. + +- Modify the example to use a typealias instead of a newtype, such as + `type MessageId = u64`. The modified example should compile, thus highlighting + the differences between the two approaches. + +- Stress that newtypes, out of the box, have no behaviour attached to them. You + need to be intentional about which methods and operators you are willing to + forward from the underlying type. In our `UserId` example, it is reasonable to + allow comparisons between `UserId`s, but it wouldn't make sense to allow + arithmetic operations like addition or subtraction. + +
diff --git a/src/idiomatic/welcome.md b/src/idiomatic/welcome.md new file mode 100644 index 00000000..e507c546 --- /dev/null +++ b/src/idiomatic/welcome.md @@ -0,0 +1,26 @@ +--- +course: Idiomatic Rust +session: Morning +target_minutes: 180 +--- + +# Welcome to Idiomatic Rust + +[Rust Fundamentals](../welcome-day-1.md) introduced Rust syntax and core +concepts. We now want to go one step further: how do you use Rust _effectively_ +in your projects? What does _idiomatic_ Rust look like? + +This course is opinionated: we will nudge you towards some patterns, and away +from others. Nonetheless, we do recognize that some projects may have different +needs. We always provide the necessary information to help you make informed +decisions within the context and constraints of your own projects. + +> ⚠️ This course is under **active development**. +> +> The material may change frequently and there might be errors that have not yet +> been spotted. Nonetheless, we encourage you to browse through and provide +> early feedback! + +## Schedule + +{{%session outline}}