diff --git a/src/SUMMARY.md b/src/SUMMARY.md index dfd360edf6b4..e32e1da3f18a 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -432,6 +432,10 @@ # Idiomatic Rust - [Welcome](idiomatic/welcome.md) +- [Foundations of API Design](idiomatic/foundations-of-api-design.md) + - [Golden Rule: Clarity & Readability](idiomatic/foundations-of-api-design/golden-rule.md) + - [Clarity: Do Provide Context](idiomatic/foundations-of-api-design/golden-rule/clarity-do-provide-context.md) + - [Readability: Consistency and Shorthand](idiomatic/foundations-of-api-design/golden-rule/readability-consistency-and-shorthands.md) - [Leveraging the Type System](idiomatic/leveraging-the-type-system.md) - [Newtype Pattern](idiomatic/leveraging-the-type-system/newtype-pattern.md) - [Semantic Confusion](idiomatic/leveraging-the-type-system/newtype-pattern/semantic-confusion.md) diff --git a/src/idiomatic/foundations-of-api-design.md b/src/idiomatic/foundations-of-api-design.md new file mode 100644 index 000000000000..6f0b137b3925 --- /dev/null +++ b/src/idiomatic/foundations-of-api-design.md @@ -0,0 +1 @@ +# Foundations of API Design diff --git a/src/idiomatic/foundations-of-api-design/golden-rule.md b/src/idiomatic/foundations-of-api-design/golden-rule.md new file mode 100644 index 000000000000..c660fa8f3aa8 --- /dev/null +++ b/src/idiomatic/foundations-of-api-design/golden-rule.md @@ -0,0 +1,54 @@ +# Golden Rule – Callsite Clarity & Readability + +A good API or a readable codebase is one that predictably follows conventions. + +```rust,editable +/// The magic function. Important for tax reasons. +fn my_magic(i: u32) -> f32 { + i as f32 + 2. +} + +/// The x function. Foundational for infrastructure reasons. +fn x(i: f32) -> String { + format!("{:.2}", (i / 3.).fract()) +} + +/// Our business logic relies on this calculation for tax reasons, +/// regulatory reasons, or critical infrastructure reasons. So if you +/// see it do be careful about changing how it's handled! +fn taxes_and_infrastructure(input: u32) -> String { + format!("{:.2}", ((input as f32 + 2.) / 3.).fract()) +} + +fn main() { + println!("{}", x(my_magic(128))); + println!("{}", taxes_and_infrastructure(128)); +} +``` + +
+ +- Writing new code is often easier than reading code, so how can we make reading + code easier? + + By making what is happening at a callsite of functions _as clear and readable + as possible_ before. + + We can't assume a reader has read and memorized all the documentation + beforehand, we need the callsite to provide as much context as possible. + +- _Calls_ to functions are going to be read far more often than the + documentation or definitions of those functions themselves. + + This is true across languages, but the communities around Rust settled on + methods to keep the process of _reading code_ reliable in certain contexts. + +- Ask before running: which function is more readable here, and why? + +- Ask: What if we remove the "good documentation" from + `taxes_and_infrastructure`? + + Without this documentation, we're only left with what's visible at the + callsite. + +
diff --git a/src/idiomatic/foundations-of-api-design/golden-rule/clarity-do-provide-context.md b/src/idiomatic/foundations-of-api-design/golden-rule/clarity-do-provide-context.md new file mode 100644 index 000000000000..adc9af35ca81 --- /dev/null +++ b/src/idiomatic/foundations-of-api-design/golden-rule/clarity-do-provide-context.md @@ -0,0 +1,66 @@ +--- +minutes: 10 +--- + +# Clarity: Do Provide Context + +Codebases are full of relationships between types, functions, inputs. Represent +them well! + +```rust +pub struct MyType { + pub my_number: u32, +} + +impl MyType { + fn add_one(&mut self) { + self.my_number += 1; + } + fn business_logic() {} +} + +mod another_package { + pub fn add_one(my_type: &mut super::MyType) { + my_type.my_number += 1; + } +} + +fn add_one(my_type: &mut MyType) { + my_type.my_number += 1; +} + +fn main() { + let mut value = MyType { my_number: 39 }; + value.add_one(); + add_one(&mut value); + another_package::add_one(&mut value); +} +``` + +
+ +- Context clues let a reader quickly understand details about what's going on. + These can be anything from descriptive names, to if a function is a method, or + where that function comes from. + +- Descriptive names are key, but can be subjective in highly specialized + business logic areas. Try to keep things + +- Demo: Ask for suggestions for what the `MyType::business_logic` method does, + then ask how we might rename the method. + +- Ask: What is the difference in what you assume about the source of the + function `add_one` when it's a method vs when it's a function that takes a + value, or when it's a function from another module? + + - We know what it does, the name is descriptive enough. + + - Potentially different authors, different packages. + + While it makes sense to keep functions as methods a lot of the time, as + there's usually an "authoritative" type, there's still plenty of reasons a + function might not be a method or static method. + + Note: Remember that a method is a relationship between a function and a type. + +
diff --git a/src/idiomatic/foundations-of-api-design/golden-rule/readability-consistency-and-shorthands.md b/src/idiomatic/foundations-of-api-design/golden-rule/readability-consistency-and-shorthands.md new file mode 100644 index 000000000000..3b00c8119c6a --- /dev/null +++ b/src/idiomatic/foundations-of-api-design/golden-rule/readability-consistency-and-shorthands.md @@ -0,0 +1,44 @@ +--- +minutes: 5 +--- + +# Readability: Consistency and Shorthands + +Be consistent in function & variable names, and use shorthands with care. + +```rust,editable +// Step 1 +fn do_thing() { /* Imagine something! */ +} +// Step 2 +fn execute_the_other_thing() {} +// Step 3 +fn anthr_thng_whch_shld_b_dn() {} + +fn main() { + do_thing(); + execute_the_other_thing(); + anthr_thng_whch_shld_b_dn(); +} +``` + +
+ +- Aim to be consistent in how things are named and abbreviated. + +- Shorthands should be used with care, and consistent across a codebase when + used. + +- This example shows three functions that all do "something." Yet each one of + them has a different naming scheme. + +- Ask: Imagine what the domain should be for these three functions. + + Expect a broad array of subjects, potential fallbacks: + +- Ask: How should they be renamed? + + Assume "do_thing" is the convention, so all other functions should start with + `do_` + +