From f41f39a0221d33caf72daac600e74e7598c0531f Mon Sep 17 00:00:00 2001 From: "Robert C. Seacord" Date: Mon, 1 Dec 2025 10:29:09 -0500 Subject: [PATCH 1/6] replaced old rule with new rule --- src/coding-guidelines/types-and-traits.rst | 116 +++++++++++++++------ 1 file changed, 87 insertions(+), 29 deletions(-) diff --git a/src/coding-guidelines/types-and-traits.rst b/src/coding-guidelines/types-and-traits.rst index dd1109dc..6a73cc6f 100644 --- a/src/coding-guidelines/types-and-traits.rst +++ b/src/coding-guidelines/types-and-traits.rst @@ -6,60 +6,118 @@ Types and Traits ================ -.. guideline:: Avoid Implicit Integer Wrapping +.. guideline:: Use strong types to differentiate between logically distinct values :id: gui_xztNdXA2oFNB - :category: required + :category: advisory :status: draft :release: 1.85.0;1.85.1 :fls: fls_cokwseo3nnr - :decidability: decidable + :decidability: undecidable :scope: module - :tags: numerics + :tags: types, safety, clarity - Code must not rely on Rust's implicit integer wrapping behavior that may occur in release - builds. Instead, explicitly handle potential overflows using the standard library's checked, - saturating, or wrapping operations. + Parameters and variables with logically distinct types must be statically distinguishable by the type system. + Use a newtype (e.g., ``struct Meters(u32);``) when: + + * Two or more quantities share the same underlying primitive representation but are logically distinct + * Confusing them would constitute a semantic error + * You need to improve type safety and encapsulation + * You need to enable trait-based behavior + * You need to establish new invariants + .. rationale:: :id: rat_kYiIiW8R2qD1 :status: draft - In debug builds, Rust performs runtime checks for integer overflow and will panic if detected. - However, in release builds (with optimizations enabled), unless the flag `overflow-checks`_ is - turned on, integer operations silently wrap around on overflow, creating potential for silent - failures and security vulnerabilities. Note that overflow-checks only brings the default panic - behavior from debug into release builds, avoiding potential silent wrap arounds. Nonetheless, - abrupt program termination is usually not suitable and, therefore, turning this flag on must - not be used as a substitute of explicit handling. Furthermore, the behavior in release mode is - under consideration by the The Rust Language Design Team and in the future overflow checking - may be turned on by default in release builds (it is a `frequently requested change`_). - - .. _overflow-checks: https://github.com/rust-lang/rust/blob/master/src/doc/rustc/src/codegen-options/index.md#overflow-checks - .. _frequently requested change: https://lang-team.rust-lang.org/frequently-requested-changes.html#numeric-overflow-checking-should-be-on-by-default-even-in-release-mode - - Safety-critical software requires consistent and predictable behavior across all build - configurations. Explicit handling of potential overflow conditions improves code clarity, - maintainability, and reduces the risk of numerical errors in production. + This rule ensures that parameters and variables convey intent directly through the type system to avoid accidental misuse of values with identical primitives but different semantics. + In particular: + * Prevents mixing logically distinct values. + Primitive types like ``u32`` or ``u64`` can represent lengths, counters, timestamps, durations, IDs, or other values. + Different semantic domains can be confused, leading to incorrect computations. + The Rust type system prevents such mistakes when semantics are encoded into distinct types. + * Improves static safety. + Statically distinct types allow the compiler to enforce domain distinctions. + Accidental swapping of parameters or returning the wrong quantity becomes a compile-time error. + * Improves readability and discoverability. + Intent-revealing names (``Meters``, ``Seconds``, ``UserId``) make code self-documenting. + Type signatures become easier to read and understand. + * Enables domain-specific trait implementations. + Statically distinct types allow you to implement ``Add``, ``Mul``, or custom traits in ways that match the domain logic. + Aliases cannot do this, because they are not distinct types. + * Supports API evolution. + Statically distinct types act as strong API contracts that can evolve independently from their underlying representations. .. non_compliant_example:: :id: non_compl_ex_PO5TyFsRTlWv :status: draft + This noncompliant example uses primitive types directly, leading to potential confusion between ``distance`` and ``time``. + Nothing prevents the caller from passing ``time`` as ``distance`` or vice-versa. + The units of each type are not clear from the function signature alone. + Mistakes compile cleanly and silently produce wrong results. + + .. code-block:: rust + + fn travel(distance: u32, time: u32) -> u32 { + distance / time + } + + fn main() { + let d: Meters = 100; + let t: Seconds = 10; + let _result = travel(t, d); // Compiles, but semantically incorrect + } + + .. non_compliant_example:: + :id: non_compl_ex_PO5TyFsRTlWu + :status: draft + + This noncompliant example uses aliases instead of distinct types. + Aliases do not create new types, so the compiler cannot enforce distinctions between ``Meters`` and ``Seconds``. + + Aliases cannot do this, because they are not distinct types. + This noncompliant example uses primitive types directly, leading to potential confusion between ``distance`` and ``time``. + Nothing prevents the caller from passing ``time`` as ``distance`` or vice-versa. + The units of each type are not clear from the function signature alone. + Mistakes compile cleanly and silently produce wrong results. + .. code-block:: rust - fn calculate_next_position(current: u32, velocity: u32) -> u32 { - // Potential for silent overflow in release builds - current + velocity + type Meters = u32; + type Seconds = u32; + type MetersPerSecond = u32; + + fn travel(distance: Meters, time: Seconds) -> MetersPerSecond { + distance / time + } + + fn main() { + let d: Meters = 100; + let t: Seconds = 10; + let _result = travel(t, d); // Compiles, but semantically incorrect } .. compliant_example:: :id: compl_ex_WTe7GoPu5Ez0 :status: draft + This compliant example uses newtypes to create distinct types for ``Meters``, ``Seconds``, and ``MetersPerSecond``. + The compiler enforces correct usage, preventing accidental swapping of parameters. + The function signature clearly conveys the intended semantics of each parameter and return value. + .. code-block:: rust - fn calculate_next_position(current: u32, velocity: u32) -> u32 { - // Explicitly handle potential overflow with checked addition - current.checked_add(velocity).expect("Position calculation overflowed") + struct Meters(u32); + struct Seconds(u32); + struct MetersPerSecond(u32); + + fn travel(distance: Meters, time: Seconds) -> MetersPerSecond { + MetersPerSecond(distance.0 / time.0) } + fn main() { + let d = Meters(100); + let t = Seconds(10); + let _result = travel(d, t); // Correct usage + } From 6646e99c03f326b188b28a5d6d7716af022c817d Mon Sep 17 00:00:00 2001 From: "Robert C. Seacord" Date: Wed, 3 Dec 2025 03:39:32 -0500 Subject: [PATCH 2/6] Apply suggestion from @rcseacord update tags --- src/coding-guidelines/types-and-traits.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coding-guidelines/types-and-traits.rst b/src/coding-guidelines/types-and-traits.rst index 6a73cc6f..f45d69c7 100644 --- a/src/coding-guidelines/types-and-traits.rst +++ b/src/coding-guidelines/types-and-traits.rst @@ -14,7 +14,7 @@ Types and Traits :fls: fls_cokwseo3nnr :decidability: undecidable :scope: module - :tags: types, safety, clarity + :tags: types, safety, understandability Parameters and variables with logically distinct types must be statically distinguishable by the type system. From 716afea895505a9155e10a5f828666c093cee05f Mon Sep 17 00:00:00 2001 From: "Robert C. Seacord" Date: Fri, 5 Dec 2025 01:00:23 -0500 Subject: [PATCH 3/6] changed IDs --- src/coding-guidelines/types-and-traits.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coding-guidelines/types-and-traits.rst b/src/coding-guidelines/types-and-traits.rst index f45d69c7..c4502209 100644 --- a/src/coding-guidelines/types-and-traits.rst +++ b/src/coding-guidelines/types-and-traits.rst @@ -7,7 +7,7 @@ Types and Traits ================ .. guideline:: Use strong types to differentiate between logically distinct values - :id: gui_xztNdXA2oFNB + :id: gui_xztNdXA2oFNC :category: advisory :status: draft :release: 1.85.0;1.85.1 @@ -27,7 +27,7 @@ Types and Traits * You need to establish new invariants .. rationale:: - :id: rat_kYiIiW8R2qD1 + :id: rat_kYiIiW8R2qD2 :status: draft This rule ensures that parameters and variables convey intent directly through the type system to avoid accidental misuse of values with identical primitives but different semantics. @@ -49,7 +49,7 @@ Types and Traits Statically distinct types act as strong API contracts that can evolve independently from their underlying representations. .. non_compliant_example:: - :id: non_compl_ex_PO5TyFsRTlWv + :id: non_compl_ex_PO5TyFsRTlWw :status: draft This noncompliant example uses primitive types directly, leading to potential confusion between ``distance`` and ``time``. @@ -70,7 +70,7 @@ Types and Traits } .. non_compliant_example:: - :id: non_compl_ex_PO5TyFsRTlWu + :id: non_compl_ex_PO5TyFsRTlWv :status: draft This noncompliant example uses aliases instead of distinct types. @@ -99,7 +99,7 @@ Types and Traits } .. compliant_example:: - :id: compl_ex_WTe7GoPu5Ez0 + :id: compl_ex_WTe7GoPu5Ez1 :status: draft This compliant example uses newtypes to create distinct types for ``Meters``, ``Seconds``, and ``MetersPerSecond``. From 17b3fa6514dcee6ebeb76f4f2f6fe57ad8a1c80d Mon Sep 17 00:00:00 2001 From: manhatsu Date: Mon, 8 Dec 2025 09:18:50 +0900 Subject: [PATCH 4/6] fix: add indentation to fit rst format --- src/coding-guidelines/types-and-traits.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coding-guidelines/types-and-traits.rst b/src/coding-guidelines/types-and-traits.rst index c4502209..141c7e6a 100644 --- a/src/coding-guidelines/types-and-traits.rst +++ b/src/coding-guidelines/types-and-traits.rst @@ -32,6 +32,7 @@ Types and Traits This rule ensures that parameters and variables convey intent directly through the type system to avoid accidental misuse of values with identical primitives but different semantics. In particular: + * Prevents mixing logically distinct values. Primitive types like ``u32`` or ``u64`` can represent lengths, counters, timestamps, durations, IDs, or other values. Different semantic domains can be confused, leading to incorrect computations. @@ -69,7 +70,7 @@ Types and Traits let _result = travel(t, d); // Compiles, but semantically incorrect } - .. non_compliant_example:: + .. non_compliant_example:: :id: non_compl_ex_PO5TyFsRTlWv :status: draft From e034e5636831a1da3b8e49d348eb04aaa4b99382 Mon Sep 17 00:00:00 2001 From: "Robert C. Seacord" Date: Wed, 10 Dec 2025 17:52:06 -0500 Subject: [PATCH 5/6] Update types-and-traits.rst --- src/coding-guidelines/types-and-traits.rst | 32 +++++++++++++++------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/coding-guidelines/types-and-traits.rst b/src/coding-guidelines/types-and-traits.rst index 141c7e6a..adcb9ade 100644 --- a/src/coding-guidelines/types-and-traits.rst +++ b/src/coding-guidelines/types-and-traits.rst @@ -65,8 +65,8 @@ Types and Traits } fn main() { - let d: Meters = 100; - let t: Seconds = 10; + let d: u32 = 100_u32; + let t: u32 = 10_u32; let _result = travel(t, d); // Compiles, but semantically incorrect } @@ -109,16 +109,28 @@ Types and Traits .. code-block:: rust + use std::ops::Div; + + #[derive(Debug, Clone, Copy)] struct Meters(u32); + + #[derive(Debug, Clone, Copy)] struct Seconds(u32); + + #[derive(Debug, Clone, Copy)] struct MetersPerSecond(u32); - fn travel(distance: Meters, time: Seconds) -> MetersPerSecond { - MetersPerSecond(distance.0 / time.0) - } + impl Div for Meters { + type Output = MetersPerSecond; - fn main() { - let d = Meters(100); - let t = Seconds(10); - let _result = travel(d, t); // Correct usage - } + fn div(self, rhs: Seconds) -> Self::Output { + MetersPerSecond(self.0 / rhs.0) + } + } + + fn main() { + let d = Meters(100); + let t = Seconds(10); + let result = d / t; // Clean and type-safe! + println!("{:?}", result); // MetersPerSecond(10) + } From b7c2bc2df9c6a733b5acaae7930c1bf30a991f60 Mon Sep 17 00:00:00 2001 From: "Robert C. Seacord" Date: Wed, 10 Dec 2025 18:04:27 -0500 Subject: [PATCH 6/6] Update types-and-traits.rst --- src/coding-guidelines/types-and-traits.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coding-guidelines/types-and-traits.rst b/src/coding-guidelines/types-and-traits.rst index adcb9ade..3ede5564 100644 --- a/src/coding-guidelines/types-and-traits.rst +++ b/src/coding-guidelines/types-and-traits.rst @@ -65,8 +65,8 @@ Types and Traits } fn main() { - let d: u32 = 100_u32; - let t: u32 = 10_u32; + let d = 100; + let t = = 10; let _result = travel(t, d); // Compiles, but semantically incorrect }