From be796c36813c5b2f33015c518248ff1905e16d2f Mon Sep 17 00:00:00 2001
From: LukeMathWalker <20745048+LukeMathWalker@users.noreply.github.com>
Date: Tue, 8 Jul 2025 17:02:42 +0200
Subject: [PATCH 01/22] Extension traits
---
src/SUMMARY.md | 4 +
.../extension-traits.md | 37 +++++++++
.../extending-foreign-traits.md | 7 ++
.../extending-foreign-types.md | 80 +++++++++++++++++++
.../method-resolution-conflicts.md | 79 ++++++++++++++++++
5 files changed, 207 insertions(+)
create mode 100644 src/idiomatic/leveraging-the-type-system/extension-traits.md
create mode 100644 src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-traits.md
create mode 100644 src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
create mode 100644 src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 1950476a423a..bc121993d98a 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -437,6 +437,10 @@
- [Semantic Confusion](idiomatic/leveraging-the-type-system/newtype-pattern/semantic-confusion.md)
- [Parse, Don't Validate](idiomatic/leveraging-the-type-system/newtype-pattern/parse-don-t-validate.md)
- [Is It Encapsulated?](idiomatic/leveraging-the-type-system/newtype-pattern/is-it-encapsulated.md)
+ - [Extension Traits](idiomatic/leveraging-the-type-system/extension-traits.md)
+ - [Extending Foreign Types](idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md)
+ - [Method Resolution Conflicts](idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md)
+ - [Extending Foreign Traits](idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-traits.md)
---
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits.md b/src/idiomatic/leveraging-the-type-system/extension-traits.md
new file mode 100644
index 000000000000..de44b5ba1b0d
--- /dev/null
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits.md
@@ -0,0 +1,37 @@
+---
+minutes: 5
+---
+
+# Extension Traits
+
+In Rust, you can't define new inherent methods for foreign types.
+
+```rust,compile_fail
+// 🛠️❌
+impl &'_ str {
+ pub fn is_palindrome(&self) -> bool {
+ self.chars().eq(self.chars().rev())
+ }
+}
+```
+
+You can use the **extension trait pattern** to work around this limitation.
+
+
+
+- Try to compile the example to show the compiler error that's emitted.
+
+ Point out, in particular, how the compiler error message nudges you towards
+ the extension trait pattern.
+
+- Explain how many type-system restrictions in Rust aim to prevent _ambiguity_.
+
+ If you were allowed to define new inherent methods on foreign types, there
+ would need to be a mechanism to disambiguate between distinct inherent methods
+ with the same name.
+
+ In particular, adding a new inherent method to a library type could cause
+ errors in downstream code if the name of the new method conflicts with an
+ inherent method that's been defined in the consuming crate.
+
+
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-traits.md b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-traits.md
new file mode 100644
index 000000000000..a56446f4c2e4
--- /dev/null
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-traits.md
@@ -0,0 +1,7 @@
+# Extending Foreign Traits
+
+- TODO: Show how extension traits can be used to extend traits rather than
+ types.
+- TODO: Show disambiguation syntax for naming conflicts between trait methods
+ and extension trait methods.
+- https://github.com/rust-lang/rfcs/blob/master/text/0132-ufcs.md
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
new file mode 100644
index 000000000000..99a9f71d9342
--- /dev/null
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
@@ -0,0 +1,80 @@
+---
+minutes: 15
+---
+
+# Extending Foreign Types
+
+An **extension trait** is a local trait definition whose primary purpose is to
+attach new methods to foreign types.
+
+```rust
+mod ext {
+ pub trait StrExt {
+ fn is_palindrome(&self) -> bool;
+ }
+
+ impl StrExt for &str {
+ fn is_palindrome(&self) -> bool {
+ self.chars().eq(self.chars().rev())
+ }
+ }
+}
+
+// Bring the extension trait into scope..
+pub use ext::StrExt as _;
+// ..then invoke its methods as if they were inherent methods
+assert!("dad".is_palindrome());
+assert!(!"grandma".is_palindrome());
+```
+
+
+
+- The `Ext` suffix is conventionally attached to the name of extension traits.
+
+ It communicates that the trait is primarily used for extension purposes, and
+ it is therefore not intended to be implemented outside the crate that defines
+ it.
+
+ Refer to the ["Extension Trait" RFC][1] as the authoritative source for naming
+ conventions.
+
+- The trait implementation for the chosen foreign type must belong to the same
+ crate where the trait is defined, otherwise you'll be blocked by Rust's
+ [_orphan rule_][2].
+
+- The extension trait must be in scope when its methods are invoked.
+
+ Comment out the `use` statement in the example to show the compiler error
+ that's emitted if you try to invoke an extension method without having the
+ corresponding extension trait in scope.
+
+- The `as _` syntax reduces the likelihood of naming conflicts when multiple
+ traits are imported. It is conventionally used when importing extension
+ traits.
+
+- Some students may be wondering: does the extension trait pattern provide
+ enough value to justify the additional boilerplate? Wouldn't a free function
+ be enough?
+
+ Show how the same example could be implemented using an `is_palindrome` free
+ function, with a single `&str` input parameter:
+
+ ```rust
+ fn is_palindrome(s: &str) -> bool {
+ s.chars().eq(s.chars().rev())
+ }
+ ```
+
+ A bespoke extension trait might be an overkill if you want to add a single
+ method to a foreign type. Both a free function and an extension trait will
+ require an additional import, and the familiarity of the method calling syntax
+ may not be enough to justify the boilerplate of a trait definition.
+
+ Nonetheless, extension methods can be **easier to discover** than free
+ functions. In particular, language servers (e.g. `rust-analyzer`) will suggest
+ extension methods if you type `.` after an instance of the foreign type.
+
+
+
+[1]: https://rust-lang.github.io/rfcs/0445-extension-trait-rfc.html
+[2]: https://github.com/rust-lang/rfcs/blob/master/text/2451-re-rebalancing-coherence.md#what-is-coherence-and-why-do-we-care
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
new file mode 100644
index 000000000000..a06ea97b07b3
--- /dev/null
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
@@ -0,0 +1,79 @@
+---
+minutes: 15
+---
+
+# Method Resolution Conflicts
+
+What happens when you have a name conflict between an inherent method and an
+extension method?
+
+```rust
+mod ext {
+ pub trait StrExt {
+ fn trim_ascii(&self) -> &str;
+ }
+
+ impl StrExt for &str {
+ fn trim_ascii(&self) -> &str {
+ self.trim_start_matches(|c: char| c.is_ascii_whitespace())
+ }
+ }
+}
+
+pub use ext::StrExt;
+// Which `trim_ascii` method is invoked?
+// The one from `StrExt`? Or the inherent one from `str`?
+assert_eq!(" dad ".trim_ascii(), "dad");
+```
+
+
+
+- The foreign type may, in a newer version, add a new inherent method with the
+ same name of our extension method.
+
+ Survey the class: what do the students think will happen in the example above?
+ Will there be a compiler error? Will one of the two methods be given higher
+ priority? Which one?
+
+ Add a `panic!("Extension trait")` in the body of `StrExt::trim_ascii` to
+ clarify which method is being invoked.
+
+- [Inherent methods have higher priority than trait methods][1], _if_ they have
+ the same name and the **same receiver**, e.g. they both expect `&self` as
+ input. The situation becomes more nuanced if the use a **different receiver**,
+ e.g. `&mut self` vs `&self`.
+
+ Change the signature of `StrExt::trim_ascii` to
+ `fn trim_ascii(&mut self) -> &str` and modify the invocation accordingly:
+
+ ```rust
+ assert_eq!((&mut " dad ").trim_ascii(), "dad");
+ ```
+
+ Now `StrExt::trim_ascii` is invoked, rather than the inherent method, since
+ `&mut self` is a more specific receiver than `&self`, the one used by the
+ inherent method.
+
+ Point the students to the Rust reference for more information on
+ [method resolution][2]. An explanation with more extensive examples can be
+ found in [an open PR to the Rust reference][3].
+
+- Avoid naming conflicts between extension trait methods and inherent methods.
+ Rust's method resolution algorithm is complex and may surprise users of your
+ code.
+
+## More to explore
+
+- The interaction between the priority search used by Rust's method resolution
+ algorithm and automatic `Deref`ering can be used to emulate
+ [specialization][4] on the stable toolchain, primarily in the context of
+ macro-generated code. Check out ["Autoref Specialization"][5] for the specific
+ details.
+
+
+
+[1]: https://doc.rust-lang.org/stable/reference/expressions/method-call-expr.html#r-expr.method.candidate-search
+[2]: https://doc.rust-lang.org/stable/reference/expressions/method-call-expr.html
+[3]: https://github.com/rust-lang/reference/pull/1725
+[4]: https://github.com/rust-lang/rust/issues/31844
+[5]: https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md
From 408962cda55e612bc625a4c3bd74fd9c8b6f5472 Mon Sep 17 00:00:00 2001
From: LukeMathWalker <20745048+LukeMathWalker@users.noreply.github.com>
Date: Wed, 9 Jul 2025 21:11:48 +0200
Subject: [PATCH 02/22] Reword
---
.../leveraging-the-type-system/extension-traits.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits.md b/src/idiomatic/leveraging-the-type-system/extension-traits.md
index de44b5ba1b0d..a502bd759995 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits.md
@@ -19,10 +19,10 @@ You can use the **extension trait pattern** to work around this limitation.
-- Try to compile the example to show the compiler error that's emitted.
+- Compile the example to show the compiler error that's emitted.
- Point out, in particular, how the compiler error message nudges you towards
- the extension trait pattern.
+ Highlight how the compiler error message nudges you towards the extension
+ trait pattern.
- Explain how many type-system restrictions in Rust aim to prevent _ambiguity_.
From 1670b6468c86be8bfb96b53452ed14be5457f598 Mon Sep 17 00:00:00 2001
From: LukeMathWalker <20745048+LukeMathWalker@users.noreply.github.com>
Date: Mon, 14 Jul 2025 15:36:42 +0200
Subject: [PATCH 03/22] Use consistent terminology
---
.../extension-traits/method-resolution-conflicts.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
index a06ea97b07b3..e899b913a69c 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
@@ -51,8 +51,8 @@ assert_eq!(" dad ".trim_ascii(), "dad");
```
Now `StrExt::trim_ascii` is invoked, rather than the inherent method, since
- `&mut self` is a more specific receiver than `&self`, the one used by the
- inherent method.
+ `&mut self` has a higher priority than `&self`, the one used by the inherent
+ method.
Point the students to the Rust reference for more information on
[method resolution][2]. An explanation with more extensive examples can be
From bd1b26db22327b73f753c25985628ba3344b509d Mon Sep 17 00:00:00 2001
From: LukeMathWalker <20745048+LukeMathWalker@users.noreply.github.com>
Date: Mon, 14 Jul 2025 17:15:18 +0200
Subject: [PATCH 04/22] Extending other traits
---
src/SUMMARY.md | 2 +-
.../extending-foreign-traits.md | 7 --
.../extending-other-traits.md | 90 +++++++++++++++++++
3 files changed, 91 insertions(+), 8 deletions(-)
delete mode 100644 src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-traits.md
create mode 100644 src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index bc121993d98a..bedd1b5dd2e8 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -440,7 +440,7 @@
- [Extension Traits](idiomatic/leveraging-the-type-system/extension-traits.md)
- [Extending Foreign Types](idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md)
- [Method Resolution Conflicts](idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md)
- - [Extending Foreign Traits](idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-traits.md)
+ - [Extending Other Traits](idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md)
---
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-traits.md b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-traits.md
deleted file mode 100644
index a56446f4c2e4..000000000000
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-traits.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Extending Foreign Traits
-
-- TODO: Show how extension traits can be used to extend traits rather than
- types.
-- TODO: Show disambiguation syntax for naming conflicts between trait methods
- and extension trait methods.
-- https://github.com/rust-lang/rfcs/blob/master/text/0132-ufcs.md
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md
new file mode 100644
index 000000000000..0a2562cefe97
--- /dev/null
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md
@@ -0,0 +1,90 @@
+---
+minutes: 10
+---
+
+# Extending Other Traits
+
+Extension traits can attach new methods to _all_ implementors of a given trait:
+
+```rust
+mod ext {
+ use std::fmt::Display;
+
+ pub trait DisplayExt {
+ fn quoted(&self) -> String;
+ }
+
+ impl DisplayExt for T {
+ fn quoted(&self) -> String {
+ format!("'{}'", self)
+ }
+ }
+}
+
+pub use ext::DisplayExt as _;
+
+assert_eq!("dad".quoted(), "'dad'");
+assert_eq!(4.quoted(), "'4'");
+assert_eq!(true.quoted(), "'true'");
+```
+
+
+
+- Highlight how we added new behaviour to _multiple_ distinct types at once.
+ `.quoted()` can be called on string slices, numbers and booleans since they
+ all implement the `Display` trait.
+
+ This flavour of the extension trait pattern is built on top of
+ [_blanket implementations_][1].
+
+ Blanket implementations allow us to implement a trait for a generic type `T`,
+ as long as it satisfies the trait bounds specified in the `impl` block. In
+ this case, the only requirement is that `T` implements the `Display` trait.
+
+- Conventionally, the extension trait is named after the trait it extends,
+ following by the `Ext` suffix. In the example above, `DisplayExt`.
+
+- There are entire libraries aimed at extending foundational traits with new
+ functionality.
+
+ [`itertools`] provides a wide range of iterator adapters and utilities via the
+ [`Itertools`] trait. [`futures`] provides [`FutureExt`] to extend the
+ [`Future`] trait.
+
+## More To Explore
+
+- Extension traits can be used by libraries to distinguish between stable and
+ experimental methods.
+
+ Stable methods are part of the trait definition.
+
+ Experimental methods are provided via an extension trait defined in a
+ different library, with a less restrictive stability policy. Some utility
+ methods are then "promoted" to the core trait definition once they have been
+ proven useful and their design has been refined.
+
+- Extension traits can be used to split a [dyn-incompatible trait][2] in two:
+
+ - A **dyn-compatible core**, restricted to the methods that satisfy
+ dyn-compatibility requirements.
+ - An **extension trait**, containing the remaining methods that are not
+ dyn-compatible. (e.g., methods with a generic parameter).
+
+- Concrete types that implement the core trait will be able to invoke all
+ methods, thanks to the blanket impl for the extension trait. Trait objects
+ (`dyn CoreTrait`) will be able to invoke all methods on the core trait as well
+ as those on the extension trait that don't require `Self: Sized`.
+
+
+
+- TODO: Show disambiguation syntax for naming conflicts between trait methods
+ and extension trait methods.
+- https://github.com/rust-lang/rfcs/blob/master/text/0132-ufcs.md
+
+[1]: https://doc.rust-lang.org/stable/reference/glossary.html#blanket-implementation
+[`itertools`]: https://docs.rs/itertools/latest/itertools/
+[`Itertools`]: https://docs.rs/itertools/latest/itertools/trait.Itertools.html
+[`futures`]: https://docs.rs/futures/latest/futures/
+[`FutureExt`]: https://docs.rs/futures/latest/futures/future/trait.FutureExt.html
+[`Future`]: https://docs.rs/futures/latest/futures/future/trait.Future.html
+[2]: https://doc.rust-lang.org/reference/items/traits.html#r-items.traits.dyn-compatible
From 06f251e78c143ac30c749560781d82f900d9cb19 Mon Sep 17 00:00:00 2001
From: LukeMathWalker <20745048+LukeMathWalker@users.noreply.github.com>
Date: Mon, 14 Jul 2025 17:25:43 +0200
Subject: [PATCH 05/22] Trait method conflicts
---
src/SUMMARY.md | 1 +
.../extending-other-traits.md | 6 +-
.../trait-method-resolution-conflicts.md | 62 +++++++++++++++++++
3 files changed, 64 insertions(+), 5 deletions(-)
create mode 100644 src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-resolution-conflicts.md
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index bedd1b5dd2e8..3e81b0c014ed 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -441,6 +441,7 @@
- [Extending Foreign Types](idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md)
- [Method Resolution Conflicts](idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md)
- [Extending Other Traits](idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md)
+ - [Trait Method Resolution Conflicts](idiomatic/leveraging-the-type-system/extension-traits/trait-method-resolution-conflicts.md)
---
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md
index 0a2562cefe97..ecbc89a028a2 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md
@@ -1,5 +1,5 @@
---
-minutes: 10
+minutes: 15
---
# Extending Other Traits
@@ -77,10 +77,6 @@ assert_eq!(true.quoted(), "'true'");
-- TODO: Show disambiguation syntax for naming conflicts between trait methods
- and extension trait methods.
-- https://github.com/rust-lang/rfcs/blob/master/text/0132-ufcs.md
-
[1]: https://doc.rust-lang.org/stable/reference/glossary.html#blanket-implementation
[`itertools`]: https://docs.rs/itertools/latest/itertools/
[`Itertools`]: https://docs.rs/itertools/latest/itertools/trait.Itertools.html
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-resolution-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-resolution-conflicts.md
new file mode 100644
index 000000000000..ac8a6a6b4c21
--- /dev/null
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-resolution-conflicts.md
@@ -0,0 +1,62 @@
+---
+minutes: 5
+---
+
+# Trait Method Resolution Conflicts
+
+What happens when you have a name conflict between two different trait methods
+implemented for the same type?
+
+```rust
+mod ext {
+ pub trait Ext1 {
+ fn is_palindrome(&self) -> bool;
+ }
+
+ pub trait Ext2 {
+ fn is_palindrome(&self) -> bool;
+ }
+
+ impl Ext1 for &str {
+ fn is_palindrome(&self) -> bool {
+ self.chars().eq(self.chars().rev())
+ }
+ }
+
+ impl Ext2 for &str {
+ fn is_palindrome(&self) -> bool {
+ self.chars().eq(self.chars().rev())
+ }
+ }
+}
+
+pub use ext::Ext1;
+pub use ext::Ext2;
+
+// Which method is invoked?
+// The one from `Ext1`? Or the one from `Ext2`?
+assert!("dad".is_palindrome());
+```
+
+
+
+- The extended trait may, in a newer version, add a new trait method with the
+ same name of our extension method.
+
+ Survey the class: what do the students think will happen in the example above?
+ Will there be a compiler error? Will one of the two methods be given higher
+ priority? Which one?
+
+- The compiler rejects the code because it cannot determine which method to
+ invoke. Neither `Ext1` nor `Ext2` has a higher priority than the other.
+
+ To resolve this conflict, you must specify which trait you want to use. For
+ example, you can call `Ext1::is_palindrome("dad")` or
+ `Ext2::is_palindrome("dad")`.
+
+ For methods with more complex signatures, you may need to use a more explicit
+ [fully-qualified syntax][1].
+
+
+
+[1]: https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls
From 21a105e147cb69851a15bf7754c5616870050dd8 Mon Sep 17 00:00:00 2001
From: LukeMathWalker <20745048+LukeMathWalker@users.noreply.github.com>
Date: Mon, 14 Jul 2025 17:36:16 +0200
Subject: [PATCH 06/22] Shorter title
---
src/SUMMARY.md | 2 +-
...method-resolution-conflicts.md => trait-method-conflicts.md} | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
rename src/idiomatic/leveraging-the-type-system/extension-traits/{trait-method-resolution-conflicts.md => trait-method-conflicts.md} (97%)
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 3e81b0c014ed..fd6ab1659d7f 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -441,7 +441,7 @@
- [Extending Foreign Types](idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md)
- [Method Resolution Conflicts](idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md)
- [Extending Other Traits](idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md)
- - [Trait Method Resolution Conflicts](idiomatic/leveraging-the-type-system/extension-traits/trait-method-resolution-conflicts.md)
+ - [Trait Method Conflicts](idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md)
---
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-resolution-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
similarity index 97%
rename from src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-resolution-conflicts.md
rename to src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
index ac8a6a6b4c21..95415b905294 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-resolution-conflicts.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
@@ -2,7 +2,7 @@
minutes: 5
---
-# Trait Method Resolution Conflicts
+# Trait Method Conflicts
What happens when you have a name conflict between two different trait methods
implemented for the same type?
From b64886c7f9c4e57a5c54218460f37569e4f8547b Mon Sep 17 00:00:00 2001
From: LukeMathWalker <20745048+LukeMathWalker@users.noreply.github.com>
Date: Mon, 14 Jul 2025 17:45:18 +0200
Subject: [PATCH 07/22] Mark trait method conflict as compile_fail
---
.../extension-traits/trait-method-conflicts.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
index 95415b905294..91817a8e9e9b 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
@@ -7,7 +7,7 @@ minutes: 5
What happens when you have a name conflict between two different trait methods
implemented for the same type?
-```rust
+```rust,compile_fail
mod ext {
pub trait Ext1 {
fn is_palindrome(&self) -> bool;
From c36f7fe24d6b4758d259700d554ea8ce27d1b009 Mon Sep 17 00:00:00 2001
From: LukeMathWalker <20745048+LukeMathWalker@users.noreply.github.com>
Date: Tue, 15 Jul 2025 11:36:59 +0200
Subject: [PATCH 08/22] Fix link
---
.../extension-traits/extending-foreign-types.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
index 99a9f71d9342..187274be92f4 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
@@ -76,5 +76,5 @@ assert!(!"grandma".is_palindrome());
-[1]: https://rust-lang.github.io/rfcs/0445-extension-trait-rfc.html
+[1]: https://rust-lang.github.io/rfcs/0445-extension-trait-conventions.html
[2]: https://github.com/rust-lang/rfcs/blob/master/text/2451-re-rebalancing-coherence.md#what-is-coherence-and-why-do-we-care
From 5ab37d8272329f7114f576c4ecee79a7dcb0c226 Mon Sep 17 00:00:00 2001
From: Luca Palmieri <20745048+LukeMathWalker@users.noreply.github.com>
Date: Tue, 15 Jul 2025 14:38:57 +0200
Subject: [PATCH 09/22] Update
src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
Co-authored-by: Dmitri Gribenko
---
.../extension-traits/extending-foreign-types.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
index 187274be92f4..5a3641467cb0 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
@@ -20,9 +20,9 @@ mod ext {
}
}
-// Bring the extension trait into scope..
+// Bring the extension trait into scope...
pub use ext::StrExt as _;
-// ..then invoke its methods as if they were inherent methods
+// ...then invoke its methods as if they were inherent methods
assert!("dad".is_palindrome());
assert!(!"grandma".is_palindrome());
```
From 841bce5f329a4c564757e992e38a3fe82ef7de97 Mon Sep 17 00:00:00 2001
From: Luca Palmieri <20745048+LukeMathWalker@users.noreply.github.com>
Date: Tue, 15 Jul 2025 14:47:36 +0200
Subject: [PATCH 10/22] Update
src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
Co-authored-by: Dmitri Gribenko
---
.../extension-traits/method-resolution-conflicts.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
index e899b913a69c..b19c520b9ddb 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
@@ -41,7 +41,7 @@ assert_eq!(" dad ".trim_ascii(), "dad");
- [Inherent methods have higher priority than trait methods][1], _if_ they have
the same name and the **same receiver**, e.g. they both expect `&self` as
input. The situation becomes more nuanced if the use a **different receiver**,
- e.g. `&mut self` vs `&self`.
+ e.g., `&mut self` vs `&self`.
Change the signature of `StrExt::trim_ascii` to
`fn trim_ascii(&mut self) -> &str` and modify the invocation accordingly:
From aa2ab0fd705b1fbc72746c9283f3391df14e7cbc Mon Sep 17 00:00:00 2001
From: Luca Palmieri <20745048+LukeMathWalker@users.noreply.github.com>
Date: Tue, 15 Jul 2025 14:47:49 +0200
Subject: [PATCH 11/22] Update
src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
Co-authored-by: Dmitri Gribenko
---
.../extension-traits/method-resolution-conflicts.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
index b19c520b9ddb..c6af0ebfb556 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
@@ -65,7 +65,7 @@ assert_eq!(" dad ".trim_ascii(), "dad");
## More to explore
- The interaction between the priority search used by Rust's method resolution
- algorithm and automatic `Deref`ering can be used to emulate
+ algorithm and automatic `Deref`ing can be used to emulate
[specialization][4] on the stable toolchain, primarily in the context of
macro-generated code. Check out ["Autoref Specialization"][5] for the specific
details.
From 63d3aa33fdb94d6f8f18c9cde72c4c311b2f607d Mon Sep 17 00:00:00 2001
From: Luca Palmieri <20745048+LukeMathWalker@users.noreply.github.com>
Date: Tue, 15 Jul 2025 14:48:07 +0200
Subject: [PATCH 12/22] Update
src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
Co-authored-by: Dmitri Gribenko
---
.../extension-traits/method-resolution-conflicts.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
index c6af0ebfb556..68378915636a 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
@@ -39,7 +39,7 @@ assert_eq!(" dad ".trim_ascii(), "dad");
clarify which method is being invoked.
- [Inherent methods have higher priority than trait methods][1], _if_ they have
- the same name and the **same receiver**, e.g. they both expect `&self` as
+ the same name and the **same receiver**, e.g., they both expect `&self` as
input. The situation becomes more nuanced if the use a **different receiver**,
e.g., `&mut self` vs `&self`.
From 17ba065295a8e0007a29ac1f41a599bc8eed7e65 Mon Sep 17 00:00:00 2001
From: LukeMathWalker <20745048+LukeMathWalker@users.noreply.github.com>
Date: Thu, 31 Jul 2025 16:52:18 +0200
Subject: [PATCH 13/22] Address review comments
---
.../extension-traits.md | 44 +++++++++++++++----
.../extending-foreign-types.md | 14 ++++--
.../trait-method-conflicts.md | 6 ++-
3 files changed, 50 insertions(+), 14 deletions(-)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits.md b/src/idiomatic/leveraging-the-type-system/extension-traits.md
index a502bd759995..1e8380f09836 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits.md
@@ -1,10 +1,14 @@
---
-minutes: 5
+minutes: 15
---
# Extension Traits
-In Rust, you can't define new inherent methods for foreign types.
+It may desirable to **extend** foreign types with new inherent methods. For
+example, allow your code to check if a string is a palindrome using
+method-calling syntax: `s.is_palindrome()`.
+
+It might feel natural to reach out for an `impl` block:
```rust,compile_fail
// 🛠️❌
@@ -15,10 +19,22 @@ impl &'_ str {
}
```
-You can use the **extension trait pattern** to work around this limitation.
+The Rust compiler won't allow it, though. But you can use the **extension trait
+pattern** to work around this limitation.
+- Start by explaining the terminology.
+
+ A Rust item (be it a trait or a type) is referred to as:
+
+ - **foreign**, if it isn't defined in the current crate
+ - **local**, if it is defined in the current crate
+
+ The distinction has significant implications for
+ [coherence and orphan rules][1], as we'll get a chance to explore in this
+ section of the course.
+
- Compile the example to show the compiler error that's emitted.
Highlight how the compiler error message nudges you towards the extension
@@ -26,12 +42,22 @@ You can use the **extension trait pattern** to work around this limitation.
- Explain how many type-system restrictions in Rust aim to prevent _ambiguity_.
- If you were allowed to define new inherent methods on foreign types, there
- would need to be a mechanism to disambiguate between distinct inherent methods
- with the same name.
+ What would happen if you were allowed to define new inherent methods on
+ foreign types? Different crates in your dependency tree might end up defining
+ different methods on the same foreign type with the same name.
+
+ As soon as there is room for ambiguity, there must be a way to disambiguate.
+ If disambiguation happens implicitly, it can lead to suprising or otherwise
+ unexpected behavior. If disambiguation happens explicitly, it can increase the
+ cognitive load on developers who are reading your code.
- In particular, adding a new inherent method to a library type could cause
- errors in downstream code if the name of the new method conflicts with an
- inherent method that's been defined in the consuming crate.
+ Furthermore, every time a crate defines a new inherent method on a foreign
+ type, it may cause compilation errors in _your_ code, as you may be forced to
+ introduce explicit disambiguation.
+
+ Rust has decided to avoid the issue altogether by forbidding the definition of
+ new inherent methods on foreign types.
+
+[1]: https://doc.rust-lang.org/stable/reference/items/implementations.html#r-items.impl.trait.orphan-rule
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
index 5a3641467cb0..83a30d32c149 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
@@ -48,9 +48,16 @@ assert!(!"grandma".is_palindrome());
that's emitted if you try to invoke an extension method without having the
corresponding extension trait in scope.
-- The `as _` syntax reduces the likelihood of naming conflicts when multiple
- traits are imported. It is conventionally used when importing extension
- traits.
+- The example above uses an [_underscore import_][3] (`use ext::StrExt as _`) to
+ minimize the likelihood of a naming conflict with other imported traits.
+
+ With an underscore import, the trait is considered to be in scope and you're
+ allowed to invoke its methods on types that implement the trait. Its _symbol_,
+ instead, is not directly accessible. This prevents you, for example, from
+ using that trait in a `where` clause.
+
+ Since extension traits aren't meant to be used in `where` clauses, they are
+ conventionally imported via an underscore import.
- Some students may be wondering: does the extension trait pattern provide
enough value to justify the additional boilerplate? Wouldn't a free function
@@ -78,3 +85,4 @@ assert!(!"grandma".is_palindrome());
[1]: https://rust-lang.github.io/rfcs/0445-extension-trait-conventions.html
[2]: https://github.com/rust-lang/rfcs/blob/master/text/2451-re-rebalancing-coherence.md#what-is-coherence-and-why-do-we-care
+[3]: https://doc.rust-lang.org/stable/reference/items/use-declarations.html#r-items.use.as-underscore
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
index 91817a8e9e9b..6379b03a8dbc 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
@@ -40,8 +40,10 @@ assert!("dad".is_palindrome());
-- The extended trait may, in a newer version, add a new trait method with the
- same name of our extension method.
+- The trait you are extending may, in a newer version, add a new trait method
+ with the same name of your extension method. Or another extension trait for
+ the same type may define a method with a name that conflicts with your own
+ extension method.
Survey the class: what do the students think will happen in the example above?
Will there be a compiler error? Will one of the two methods be given higher
From d10c9859fca1435d9b286929919925be3f162470 Mon Sep 17 00:00:00 2001
From: LukeMathWalker <20745048+LukeMathWalker@users.noreply.github.com>
Date: Thu, 31 Jul 2025 16:55:45 +0200
Subject: [PATCH 14/22] Formatting and typos
---
.../leveraging-the-type-system/extension-traits.md | 2 +-
.../extension-traits/method-resolution-conflicts.md | 7 +++----
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits.md b/src/idiomatic/leveraging-the-type-system/extension-traits.md
index 1e8380f09836..f5bc3025b189 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits.md
@@ -47,7 +47,7 @@ pattern** to work around this limitation.
different methods on the same foreign type with the same name.
As soon as there is room for ambiguity, there must be a way to disambiguate.
- If disambiguation happens implicitly, it can lead to suprising or otherwise
+ If disambiguation happens implicitly, it can lead to surprising or otherwise
unexpected behavior. If disambiguation happens explicitly, it can increase the
cognitive load on developers who are reading your code.
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
index 68378915636a..df91c0e58360 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
@@ -65,10 +65,9 @@ assert_eq!(" dad ".trim_ascii(), "dad");
## More to explore
- The interaction between the priority search used by Rust's method resolution
- algorithm and automatic `Deref`ing can be used to emulate
- [specialization][4] on the stable toolchain, primarily in the context of
- macro-generated code. Check out ["Autoref Specialization"][5] for the specific
- details.
+ algorithm and automatic `Deref`ing can be used to emulate [specialization][4]
+ on the stable toolchain, primarily in the context of macro-generated code.
+ Check out ["Autoref Specialization"][5] for the specific details.
From 771a37a8c9c561a4d149f52d4a9ac8bcc39332ca Mon Sep 17 00:00:00 2001
From: LukeMathWalker <20745048+LukeMathWalker@users.noreply.github.com>
Date: Thu, 31 Jul 2025 17:09:40 +0200
Subject: [PATCH 15/22] Elaborate further on the desired goal when extending
other traits
---
.../extension-traits/extending-other-traits.md | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md
index ecbc89a028a2..d8d4e8ba34fd 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md
@@ -4,7 +4,8 @@ minutes: 15
# Extending Other Traits
-Extension traits can attach new methods to _all_ implementors of a given trait:
+As with types, it may be desirable to **extend foreign traits**. In particular,
+to attach new methods to _all_ implementors of a given trait.
```rust
mod ext {
@@ -41,6 +42,14 @@ assert_eq!(true.quoted(), "'true'");
as long as it satisfies the trait bounds specified in the `impl` block. In
this case, the only requirement is that `T` implements the `Display` trait.
+- Draw the students attention to the implementation of `DisplayExt::quoted`: we
+ can't make any assumptions about the type of `T` other than that it implements
+ `Display`. All our logic must either use methods from `Display` or
+ functions/macros that doesn't require `T` to implement any other trait.
+
+ We could introduce additional trait bounds on `T`, but it would restrict the
+ set of types that can leverage the extension trait.
+
- Conventionally, the extension trait is named after the trait it extends,
following by the `Ext` suffix. In the example above, `DisplayExt`.
From b58a2a52135dcb259e8a433361483ec387565723 Mon Sep 17 00:00:00 2001
From: LukeMathWalker <20745048+LukeMathWalker@users.noreply.github.com>
Date: Thu, 31 Jul 2025 17:16:52 +0200
Subject: [PATCH 16/22] Extract bullet point into its own slide
---
src/SUMMARY.md | 1 +
.../extending-foreign-types.md | 24 +----------
.../should-i-define-an-extension-trait.md | 40 +++++++++++++++++++
3 files changed, 42 insertions(+), 23 deletions(-)
create mode 100644 src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index fd6ab1659d7f..6c22e06eff86 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -440,6 +440,7 @@
- [Extension Traits](idiomatic/leveraging-the-type-system/extension-traits.md)
- [Extending Foreign Types](idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md)
- [Method Resolution Conflicts](idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md)
+ - [Should I Define An Extension Trait?](idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md)
- [Extending Other Traits](idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md)
- [Trait Method Conflicts](idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
index 83a30d32c149..bf781700f09a 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
@@ -1,5 +1,5 @@
---
-minutes: 15
+minutes: 10
---
# Extending Foreign Types
@@ -59,28 +59,6 @@ assert!(!"grandma".is_palindrome());
Since extension traits aren't meant to be used in `where` clauses, they are
conventionally imported via an underscore import.
-- Some students may be wondering: does the extension trait pattern provide
- enough value to justify the additional boilerplate? Wouldn't a free function
- be enough?
-
- Show how the same example could be implemented using an `is_palindrome` free
- function, with a single `&str` input parameter:
-
- ```rust
- fn is_palindrome(s: &str) -> bool {
- s.chars().eq(s.chars().rev())
- }
- ```
-
- A bespoke extension trait might be an overkill if you want to add a single
- method to a foreign type. Both a free function and an extension trait will
- require an additional import, and the familiarity of the method calling syntax
- may not be enough to justify the boilerplate of a trait definition.
-
- Nonetheless, extension methods can be **easier to discover** than free
- functions. In particular, language servers (e.g. `rust-analyzer`) will suggest
- extension methods if you type `.` after an instance of the foreign type.
-
[1]: https://rust-lang.github.io/rfcs/0445-extension-trait-conventions.html
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md b/src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md
new file mode 100644
index 000000000000..e8d367ec1221
--- /dev/null
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md
@@ -0,0 +1,40 @@
+---
+minutes: 5
+---
+
+# Should I Define An Extension Trait?
+
+In what scenarios should you prefer an extension trait over a free function?
+
+```rust
+pub trait StrExt {
+ fn is_palindrome(&self) -> bool;
+}
+
+impl StrExt for &str {
+ fn is_palindrome(&self) -> bool {
+ self.chars().eq(self.chars().rev())
+ }
+}
+
+// vs
+
+fn is_palindrome(s: &str) -> bool {
+ s.chars().eq(s.chars().rev())
+}
+```
+
+The main advantage of extension traits is **ease of discovery**.
+
+
+
+- A bespoke extension trait might be an overkill if you want to add a single
+ method to a foreign type. Both a free function and an extension trait will
+ require an additional import, and the familiarity of the method calling syntax
+ may not be enough to justify the boilerplate of a trait definition.
+
+ Nonetheless, extension methods can be **easier to discover** than free
+ functions. In particular, language servers (e.g. `rust-analyzer`) will suggest
+ extension methods if you type `.` after an instance of the foreign type.
+
+
From d141a8d1ce7b5947b422ca0779097de2da8607c3 Mon Sep 17 00:00:00 2001
From: tall-vase <228449146+tall-vase@users.noreply.github.com>
Date: Tue, 14 Oct 2025 10:40:17 +0100
Subject: [PATCH 17/22] Apply suggestions from code review
Co-authored-by: Dmitri Gribenko
Co-authored-by: Nicole L
---
.../extension-traits.md | 6 ++--
.../extending-foreign-types.md | 3 +-
.../extending-other-traits.md | 29 ++++++++++---------
.../method-resolution-conflicts.md | 7 ++---
.../should-i-define-an-extension-trait.md | 11 ++-----
.../trait-method-conflicts.md | 4 +--
6 files changed, 27 insertions(+), 33 deletions(-)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits.md b/src/idiomatic/leveraging-the-type-system/extension-traits.md
index f5bc3025b189..4655d6dfc5b9 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits.md
@@ -24,9 +24,7 @@ pattern** to work around this limitation.
-- Start by explaining the terminology.
-
- A Rust item (be it a trait or a type) is referred to as:
+- A Rust item (be it a trait or a type) is referred to as:
- **foreign**, if it isn't defined in the current crate
- **local**, if it is defined in the current crate
@@ -57,6 +55,8 @@ pattern** to work around this limitation.
Rust has decided to avoid the issue altogether by forbidding the definition of
new inherent methods on foreign types.
+
+- Other languages (e.g, Kotlin, C#, Swift) allow adding methods to existing types, often called "extension methods." This leads to different trade-offs in terms of potential ambiguities and the need for global reasoning.
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
index bf781700f09a..96a805d58e57 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
@@ -38,8 +38,7 @@ assert!(!"grandma".is_palindrome());
Refer to the ["Extension Trait" RFC][1] as the authoritative source for naming
conventions.
-- The trait implementation for the chosen foreign type must belong to the same
- crate where the trait is defined, otherwise you'll be blocked by Rust's
+- The extension trait implementation for a foreign type must be in the same crate as the trait itself, otherwise you'll be blocked by Rust's
[_orphan rule_][2].
- The extension trait must be in scope when its methods are invoked.
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md
index d8d4e8ba34fd..832c43f308de 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md
@@ -31,34 +31,35 @@ assert_eq!(true.quoted(), "'true'");
-- Highlight how we added new behaviour to _multiple_ distinct types at once.
+- Highlight how we added new behavior to _multiple_ types at once.
`.quoted()` can be called on string slices, numbers and booleans since they
all implement the `Display` trait.
- This flavour of the extension trait pattern is built on top of
+ This flavour of the extension trait pattern uses
[_blanket implementations_][1].
- Blanket implementations allow us to implement a trait for a generic type `T`,
- as long as it satisfies the trait bounds specified in the `impl` block. In
+ A blanket implementation implements a trait for all types `T`
+ that satisfy the trait bounds specified in the `impl` block. In
this case, the only requirement is that `T` implements the `Display` trait.
-- Draw the students attention to the implementation of `DisplayExt::quoted`: we
- can't make any assumptions about the type of `T` other than that it implements
+- Draw the students' attention to the implementation of `DisplayExt::quoted`: we
+ can't make any assumptions about `T` other than that it implements
`Display`. All our logic must either use methods from `Display` or
- functions/macros that doesn't require `T` to implement any other trait.
+ functions/macros that don't require other traits..
+ For example, we can call `format!` with `T`, but can't call `.to_uppercase()` because it is not necessarily a `String`.
+
We could introduce additional trait bounds on `T`, but it would restrict the
set of types that can leverage the extension trait.
- Conventionally, the extension trait is named after the trait it extends,
- following by the `Ext` suffix. In the example above, `DisplayExt`.
+ followed by the `Ext` suffix. In the example above, `DisplayExt`.
-- There are entire libraries aimed at extending foundational traits with new
- functionality.
+- There are entire crates that extend standard library traits with new functionality.
- [`itertools`] provides a wide range of iterator adapters and utilities via the
- [`Itertools`] trait. [`futures`] provides [`FutureExt`] to extend the
- [`Future`] trait.
+ - `itertools` crate provides the `Itertools` trait that extends `Iterator`. It adds many iterator adapters, such as `interleave` and `unique`. It provides new algorithmic building blocks for iterator pipelines built with method chaining.
+
+ - `futures` crate provides the `FutureExt` trait, which extends the `Future` trait with new combinators and helper methods.
## More To Explore
@@ -77,7 +78,7 @@ assert_eq!(true.quoted(), "'true'");
- A **dyn-compatible core**, restricted to the methods that satisfy
dyn-compatibility requirements.
- An **extension trait**, containing the remaining methods that are not
- dyn-compatible. (e.g., methods with a generic parameter).
+ dyn-compatible (e.g., methods with a generic parameter).
- Concrete types that implement the core trait will be able to invoke all
methods, thanks to the blanket impl for the extension trait. Trait objects
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
index df91c0e58360..c70c021c3e69 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
@@ -29,7 +29,7 @@ assert_eq!(" dad ".trim_ascii(), "dad");
- The foreign type may, in a newer version, add a new inherent method with the
- same name of our extension method.
+ same name as our extension method.
Survey the class: what do the students think will happen in the example above?
Will there be a compiler error? Will one of the two methods be given higher
@@ -40,7 +40,7 @@ assert_eq!(" dad ".trim_ascii(), "dad");
- [Inherent methods have higher priority than trait methods][1], _if_ they have
the same name and the **same receiver**, e.g., they both expect `&self` as
- input. The situation becomes more nuanced if the use a **different receiver**,
+ input. The situation becomes more nuanced if they use a **different receiver**,
e.g., `&mut self` vs `&self`.
Change the signature of `StrExt::trim_ascii` to
@@ -55,8 +55,7 @@ assert_eq!(" dad ".trim_ascii(), "dad");
method.
Point the students to the Rust reference for more information on
- [method resolution][2]. An explanation with more extensive examples can be
- found in [an open PR to the Rust reference][3].
+ [method resolution][2].
- Avoid naming conflicts between extension trait methods and inherent methods.
Rust's method resolution algorithm is complex and may surprise users of your
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md b/src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md
index e8d367ec1221..7a1cb357bc6e 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md
@@ -28,13 +28,8 @@ The main advantage of extension traits is **ease of discovery**.
-- A bespoke extension trait might be an overkill if you want to add a single
- method to a foreign type. Both a free function and an extension trait will
- require an additional import, and the familiarity of the method calling syntax
- may not be enough to justify the boilerplate of a trait definition.
-
- Nonetheless, extension methods can be **easier to discover** than free
- functions. In particular, language servers (e.g. `rust-analyzer`) will suggest
- extension methods if you type `.` after an instance of the foreign type.
+- Extension methods can be easier to discover than free functions. Language servers (e.g., `rust-analyzer`) will suggest them if you type `.` after an instance of the foreign type.
+
+- However, a bespoke extension trait might be overkill for a single method. Both approaches require an additional import, and the familiar method syntax may not justify the boilerplate of a full trait definition.
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
index 6379b03a8dbc..4f56bc2ac9e0 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
@@ -41,7 +41,7 @@ assert!("dad".is_palindrome());
- The trait you are extending may, in a newer version, add a new trait method
- with the same name of your extension method. Or another extension trait for
+ with the same name as your extension method. Or another extension trait for
the same type may define a method with a name that conflicts with your own
extension method.
@@ -54,7 +54,7 @@ assert!("dad".is_palindrome());
To resolve this conflict, you must specify which trait you want to use. For
example, you can call `Ext1::is_palindrome("dad")` or
- `Ext2::is_palindrome("dad")`.
+ `Ext2::is_palindrome("dad")`. Demonstrate this syntax and that the updated code compiles.
For methods with more complex signatures, you may need to use a more explicit
[fully-qualified syntax][1].
From a0146cc54b3387957029e8064ddfd74eee63dd41 Mon Sep 17 00:00:00 2001
From: tall-vase
Date: Thu, 16 Oct 2025 09:58:18 +0100
Subject: [PATCH 18/22] Address feedback + grammar check
---
src/SUMMARY.md | 4 +-
.../extension-traits.md | 6 +-
.../extending-foreign-types.md | 7 +-
.../extending-other-traits.md | 38 +++++-----
.../method-resolution-conflicts.md | 69 ++++++++++++-------
.../should-i-define-an-extension-trait.md | 26 ++++++-
.../trait-method-conflicts.md | 8 +--
7 files changed, 103 insertions(+), 55 deletions(-)
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 6c22e06eff86..bc162e8569c9 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -440,9 +440,9 @@
- [Extension Traits](idiomatic/leveraging-the-type-system/extension-traits.md)
- [Extending Foreign Types](idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md)
- [Method Resolution Conflicts](idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md)
- - [Should I Define An Extension Trait?](idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md)
- - [Extending Other Traits](idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md)
- [Trait Method Conflicts](idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md)
+ - [Extending Other Traits](idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md)
+ - [Should I Define An Extension Trait?](idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md)
---
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits.md b/src/idiomatic/leveraging-the-type-system/extension-traits.md
index 4655d6dfc5b9..563a640467c5 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits.md
@@ -55,8 +55,10 @@ pattern** to work around this limitation.
Rust has decided to avoid the issue altogether by forbidding the definition of
new inherent methods on foreign types.
-
-- Other languages (e.g, Kotlin, C#, Swift) allow adding methods to existing types, often called "extension methods." This leads to different trade-offs in terms of potential ambiguities and the need for global reasoning.
+
+- Other languages (e.g, Kotlin, C#, Swift) allow adding methods to existing
+ types, often called "extension methods." This leads to different trade-offs in
+ terms of potential ambiguities and the need for global reasoning.
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
index 96a805d58e57..48fb474be6bd 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-foreign-types.md
@@ -38,7 +38,8 @@ assert!(!"grandma".is_palindrome());
Refer to the ["Extension Trait" RFC][1] as the authoritative source for naming
conventions.
-- The extension trait implementation for a foreign type must be in the same crate as the trait itself, otherwise you'll be blocked by Rust's
+- The extension trait implementation for a foreign type must be in the same
+ crate as the trait itself, otherwise you'll be blocked by Rust's
[_orphan rule_][2].
- The extension trait must be in scope when its methods are invoked.
@@ -47,8 +48,8 @@ assert!(!"grandma".is_palindrome());
that's emitted if you try to invoke an extension method without having the
corresponding extension trait in scope.
-- The example above uses an [_underscore import_][3] (`use ext::StrExt as _`) to
- minimize the likelihood of a naming conflict with other imported traits.
+- The example above uses an [_underscore import_][3] (`use ext::StringExt as _`)
+ to minimize the likelihood of a naming conflict with other imported traits.
With an underscore import, the trait is considered to be in scope and you're
allowed to invoke its methods on types that implement the trait. Its _symbol_,
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md
index 832c43f308de..87b82c6a29d7 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/extending-other-traits.md
@@ -31,35 +31,41 @@ assert_eq!(true.quoted(), "'true'");
-- Highlight how we added new behavior to _multiple_ types at once.
- `.quoted()` can be called on string slices, numbers and booleans since they
- all implement the `Display` trait.
+- Highlight how we added new behavior to _multiple_ types at once. `.quoted()`
+ can be called on string slices, numbers, and booleans since they all implement
+ the `Display` trait.
- This flavour of the extension trait pattern uses
+ This flavor of the extension trait pattern uses
[_blanket implementations_][1].
- A blanket implementation implements a trait for all types `T`
- that satisfy the trait bounds specified in the `impl` block. In
- this case, the only requirement is that `T` implements the `Display` trait.
+ A blanket implementation implements a trait for all types `T` that satisfy the
+ trait bounds specified in the `impl` block. In this case, the only requirement
+ is that `T` implements the `Display` trait.
- Draw the students' attention to the implementation of `DisplayExt::quoted`: we
- can't make any assumptions about `T` other than that it implements
- `Display`. All our logic must either use methods from `Display` or
- functions/macros that don't require other traits..
+ can't make any assumptions about `T` other than that it implements `Display`.
+ All our logic must either use methods from `Display` or functions/macros that
+ don't require other traits.
+
+ For example, we can call `format!` with `T`, but can't call `.to_uppercase()`
+ because it is not necessarily a `String`.
- For example, we can call `format!` with `T`, but can't call `.to_uppercase()` because it is not necessarily a `String`.
-
We could introduce additional trait bounds on `T`, but it would restrict the
set of types that can leverage the extension trait.
- Conventionally, the extension trait is named after the trait it extends,
followed by the `Ext` suffix. In the example above, `DisplayExt`.
-- There are entire crates that extend standard library traits with new functionality.
+- There are entire crates that extend standard library traits with new
+ functionality.
+
+ - `itertools` crate provides the `Itertools` trait that extends `Iterator`. It
+ adds many iterator adapters, such as `interleave` and `unique`. It provides
+ new algorithmic building blocks for iterator pipelines built with method
+ chaining.
- - `itertools` crate provides the `Itertools` trait that extends `Iterator`. It adds many iterator adapters, such as `interleave` and `unique`. It provides new algorithmic building blocks for iterator pipelines built with method chaining.
-
- - `futures` crate provides the `FutureExt` trait, which extends the `Future` trait with new combinators and helper methods.
+ - `futures` crate provides the `FutureExt` trait, which extends the `Future`
+ trait with new combinators and helper methods.
## More To Explore
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
index c70c021c3e69..c315ea64603d 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/method-resolution-conflicts.md
@@ -7,53 +7,72 @@ minutes: 15
What happens when you have a name conflict between an inherent method and an
extension method?
-```rust
+```rust,editable
mod ext {
- pub trait StrExt {
- fn trim_ascii(&self) -> &str;
+ pub trait CountOnesExt {
+ fn count_ones(&self) -> u32;
}
- impl StrExt for &str {
- fn trim_ascii(&self) -> &str {
- self.trim_start_matches(|c: char| c.is_ascii_whitespace())
+ impl CountOnesExt for i32 {
+ fn count_ones(&self) -> u32 {
+ let value = *self;
+ (0..32).filter(|i| ((value >> i) & 1i32) == 1).count() as u32
}
}
}
-
-pub use ext::StrExt;
-// Which `trim_ascii` method is invoked?
-// The one from `StrExt`? Or the inherent one from `str`?
-assert_eq!(" dad ".trim_ascii(), "dad");
+fn main() {
+ pub use ext::CountOnesExt;
+ // Which `count_ones` method is invoked?
+ // The one from `CountOnesExt`? Or the inherent one from `i32`?
+ assert_eq!((-1i32).count_ones(), 32);
+}
```
-- The foreign type may, in a newer version, add a new inherent method with the
+- A foreign type may, in a newer version, add a new inherent method with the
same name as our extension method.
- Survey the class: what do the students think will happen in the example above?
- Will there be a compiler error? Will one of the two methods be given higher
- priority? Which one?
+ Ask: What will happen in the example above? Will there be a compiler error?
+ Will one of the two methods be given higher priority? Which one?
+
+ Add a `panic!("Extension trait");` in the body of `CountOnesExt::count_ones`
+ to clarify which method is being invoked.
- Add a `panic!("Extension trait")` in the body of `StrExt::trim_ascii` to
- clarify which method is being invoked.
+- To prevent users of the Rust language from having to manually specify which
+ method to use in all cases, there is a priority ordering system for how
+ methods get "picked" first:
+ - Immutable (`&self`) first
+ - Inherent (method defined in the type's `impl` block) before Trait (method
+ added by a trait impl).
+ - Mutable (`&mut self`) Second
+ - Inherent before Trait.
-- [Inherent methods have higher priority than trait methods][1], _if_ they have
- the same name and the **same receiver**, e.g., they both expect `&self` as
- input. The situation becomes more nuanced if they use a **different receiver**,
- e.g., `&mut self` vs `&self`.
+ If every method with the same name has different mutability and was either
+ defined in as an inherent method or trait method, with no overlap, this makes
+ the job easy for the compiler.
- Change the signature of `StrExt::trim_ascii` to
- `fn trim_ascii(&mut self) -> &str` and modify the invocation accordingly:
+ This does introduce some ambiguity for the user, who may be confused as to why
+ a method they're relying on is not producing expected behavior. Avoid name
+ conflicts instead of relying on this mechanism if you can.
+
+ Demonstrate: Change the signature and implementation of
+ `CountOnesExt::count_ones` to `fn count_ones(&mut self) -> u32` and modify the
+ invocation accordingly:
```rust
- assert_eq!((&mut " dad ").trim_ascii(), "dad");
+ assert_eq!((&mut -1i32).count_ones(), 32);
```
- Now `StrExt::trim_ascii` is invoked, rather than the inherent method, since
+ `CountOnesExt::count_ones` is invoked, rather than the inherent method, since
`&mut self` has a higher priority than `&self`, the one used by the inherent
method.
+ If an immutable inherent method and a mutable trait method exist for the same
+ type, we can specify which one to use at the call site by using
+ `(&).count_ones()` to get the immutable (higher priority) method or
+ `(&mut ).count_ones()`
+
Point the students to the Rust reference for more information on
[method resolution][2].
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md b/src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md
index 7a1cb357bc6e..f4224dcc677f 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md
@@ -28,8 +28,28 @@ The main advantage of extension traits is **ease of discovery**.
-- Extension methods can be easier to discover than free functions. Language servers (e.g., `rust-analyzer`) will suggest them if you type `.` after an instance of the foreign type.
-
-- However, a bespoke extension trait might be overkill for a single method. Both approaches require an additional import, and the familiar method syntax may not justify the boilerplate of a full trait definition.
+- Extension methods can be easier to discover than free functions. Language
+ servers (e.g., `rust-analyzer`) will suggest them if you type `.` after an
+ instance of the foreign type.
+
+- However, a bespoke extension trait might be overkill for a single method. Both
+ approaches require an additional import, and the familiar method syntax may
+ not justify the boilerplate of a full trait definition.
+
+- **Discoverability:** Extension methods are easier to discover than free
+ functions. Language servers (e.g., `rust-analyzer`) will suggest them if you
+ type `.` after an instance of the foreign type.
+- **Method Chaining:** A major ergonomic win for extension traits is method
+ chaining. This is the foundation of the `Iterator` trait, allowing for fluent
+ calls like `data.iter().filter(...).map(...)`. Achieving this with free
+ functions would be far more cumbersome (`map(filter(iter(data), ...), ...)`).
+- **API Cohesion:** Extension traits help create a cohesive API. If you have
+ several related functions for a foreign type (e.g., `is_palindrome`,
+ `word_count`, `to_kebab_case`), grouping them in a single `StrExt` trait is
+ often cleaner than having multiple free functions for a user to import.
+- **Trade-offs:** Despite these advantages, a bespoke extension trait might be
+ overkill for a single, simple function. Both approaches require an additional
+ import, and the familiar method syntax may not justify the boilerplate of a
+ full trait definition.
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
index 4f56bc2ac9e0..d90469b89147 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
@@ -45,16 +45,16 @@ assert!("dad".is_palindrome());
the same type may define a method with a name that conflicts with your own
extension method.
- Survey the class: what do the students think will happen in the example above?
- Will there be a compiler error? Will one of the two methods be given higher
- priority? Which one?
+ Ask: what will happen in the example above? Will there be a compiler error?
+ Will one of the two methods be given higher priority? Which one?
- The compiler rejects the code because it cannot determine which method to
invoke. Neither `Ext1` nor `Ext2` has a higher priority than the other.
To resolve this conflict, you must specify which trait you want to use. For
example, you can call `Ext1::is_palindrome("dad")` or
- `Ext2::is_palindrome("dad")`. Demonstrate this syntax and that the updated code compiles.
+ `Ext2::is_palindrome("dad")`. Demonstrate this syntax and that the updated
+ code compiles.
For methods with more complex signatures, you may need to use a more explicit
[fully-qualified syntax][1].
From 85a2e5f4e0c39de03b1af977cd4c74a5cd7a9b31 Mon Sep 17 00:00:00 2001
From: tall-vase <228449146+tall-vase@users.noreply.github.com>
Date: Mon, 20 Oct 2025 08:52:14 +0100
Subject: [PATCH 19/22] Space out paragraphs
Co-authored-by: Dmitri Gribenko
---
.../extension-traits/should-i-define-an-extension-trait.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md b/src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md
index f4224dcc677f..2db13fb1c45c 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/should-i-define-an-extension-trait.md
@@ -39,14 +39,17 @@ The main advantage of extension traits is **ease of discovery**.
- **Discoverability:** Extension methods are easier to discover than free
functions. Language servers (e.g., `rust-analyzer`) will suggest them if you
type `.` after an instance of the foreign type.
+
- **Method Chaining:** A major ergonomic win for extension traits is method
chaining. This is the foundation of the `Iterator` trait, allowing for fluent
calls like `data.iter().filter(...).map(...)`. Achieving this with free
functions would be far more cumbersome (`map(filter(iter(data), ...), ...)`).
+
- **API Cohesion:** Extension traits help create a cohesive API. If you have
several related functions for a foreign type (e.g., `is_palindrome`,
`word_count`, `to_kebab_case`), grouping them in a single `StrExt` trait is
often cleaner than having multiple free functions for a user to import.
+
- **Trade-offs:** Despite these advantages, a bespoke extension trait might be
overkill for a single, simple function. Both approaches require an additional
import, and the familiar method syntax may not justify the boilerplate of a
From 469a04076bd3e15dfdf8aab3516ea8d807f31d81 Mon Sep 17 00:00:00 2001
From: tall-vase
Date: Mon, 20 Oct 2025 10:41:31 +0100
Subject: [PATCH 20/22] Address feedback wrt demonstration of trait method
conflict slide
---
.../trait-method-conflicts.md | 23 ++++++++++++-------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
index d90469b89147..b46ba3e5aa76 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
@@ -7,7 +7,9 @@ minutes: 5
What happens when you have a name conflict between two different trait methods
implemented for the same type?
-```rust,compile_fail
+
+
+```rust,editable,compile_fail
mod ext {
pub trait Ext1 {
fn is_palindrome(&self) -> bool;
@@ -30,12 +32,13 @@ mod ext {
}
}
-pub use ext::Ext1;
-pub use ext::Ext2;
+pub use ext::{Ext1, Ext2};
// Which method is invoked?
// The one from `Ext1`? Or the one from `Ext2`?
-assert!("dad".is_palindrome());
+fn main() {
+ assert!("dad".is_palindrome());
+}
```
@@ -51,14 +54,18 @@ assert!("dad".is_palindrome());
- The compiler rejects the code because it cannot determine which method to
invoke. Neither `Ext1` nor `Ext2` has a higher priority than the other.
- To resolve this conflict, you must specify which trait you want to use. For
- example, you can call `Ext1::is_palindrome("dad")` or
- `Ext2::is_palindrome("dad")`. Demonstrate this syntax and that the updated
- code compiles.
+ To resolve this conflict, you must specify which trait you want to use.
+
+ Demonstrate: call `Ext1::is_palindrome(&"dad")` or
+ `Ext2::is_palindrome(&"dad")` instead of `"dad".is_palindrome()`.
For methods with more complex signatures, you may need to use a more explicit
[fully-qualified syntax][1].
+- Demonstrate: replace `"dad".is_palindrome()` with
+ `<&str as Ext1>::is_palindrome(&"dad")` or<&str as
+ Ext2>::is_palindrome(&"dad")`.
+
[1]: https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls
From 3d17013a62e9bbcff19269a820562182f3c8eb27 Mon Sep 17 00:00:00 2001
From: tall-vase <228449146+tall-vase@users.noreply.github.com>
Date: Mon, 20 Oct 2025 11:17:11 +0100
Subject: [PATCH 21/22] Update
src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
Co-authored-by: Dmitri Gribenko
---
.../extension-traits/trait-method-conflicts.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
index b46ba3e5aa76..bb86b53d0c2d 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
@@ -63,7 +63,7 @@ fn main() {
[fully-qualified syntax][1].
- Demonstrate: replace `"dad".is_palindrome()` with
- `<&str as Ext1>::is_palindrome(&"dad")` or<&str as
+ `<&str as Ext1>::is_palindrome(&"dad")` or `<&str as
Ext2>::is_palindrome(&"dad")`.
From 669c74aebe19942724a4967ac5300deaf3c973ed Mon Sep 17 00:00:00 2001
From: tall-vase
Date: Mon, 20 Oct 2025 11:25:23 +0100
Subject: [PATCH 22/22] Formatting pass
---
.../extension-traits/trait-method-conflicts.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
index bb86b53d0c2d..051128c2abf7 100644
--- a/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
+++ b/src/idiomatic/leveraging-the-type-system/extension-traits/trait-method-conflicts.md
@@ -63,7 +63,8 @@ fn main() {
[fully-qualified syntax][1].
- Demonstrate: replace `"dad".is_palindrome()` with
- `<&str as Ext1>::is_palindrome(&"dad")` or `<&str as
+ `<&str as Ext1>::is_palindrome(&"dad")` or
+ `<&str as
Ext2>::is_palindrome(&"dad")`.