From 436a4e96b3a9b36a8e31bf34e8e0ff178963be55 Mon Sep 17 00:00:00 2001 From: x0rw Date: Sun, 11 May 2025 17:29:08 +0100 Subject: [PATCH 1/4] guideline: a macro should not be used in place of a function --- src/coding-guidelines/macros.rst | 67 +++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/src/coding-guidelines/macros.rst b/src/coding-guidelines/macros.rst index e03256b6..93ee52ef 100644 --- a/src/coding-guidelines/macros.rst +++ b/src/coding-guidelines/macros.rst @@ -90,6 +90,71 @@ Macros // TODO +.. guideline:: a macro should not be used in place of a function + :id: gui_2jjWUoF1teOY + :category: mandatory + :status: draft + :release: todo + :fls: fls_xa7lp0zg1ol2 + :decidability: decidable + :scope: system + :tags: reduce-human-error + + Functions should always be preferred over macros, except when macros provide essential functionality that functions cannot, such as variadic interfaces, compile-time code generation, or syntax extensions via custom derive and attribute macros. + + | + + .. rationale:: + :id: rat_M9bp23ctkzQ7 + :status: draft + + Macros are powerful but they come at the cost of readability, complexity, and maintainability. They obfuscate control flow and type signatures, + + **Debugging Complexity** + + - Errors point to expanded code rather than source locations, making it difficult to trace compile-time errors back to the original macro invocation. + + **Optimization** + + - Macros may inhibit compiler optimizations that work better with functions. + - Macros act like ``#[inline(always)]`` functions, which can lead to code bloat. + - They don't benefit from the compiler's inlining heuristics, missing out on selective inlining where the compiler decides when inlining is beneficial. + + **Functions provide** + + - Clear type signatures. + - Predictable behavior. + - Proper stack traces. + - Consistent optimization opportunities. + + + .. non_compliant_example:: + :id: non_compl_ex_TZgk2vG42t2r + :status: draft + + Using a macro where a simple function would suffice: + + .. code-block:: rust + + macro_rules! add { + ($a:expr, $b:expr) => { $a + $b }; + } + + .. compliant_example:: + :id: compl_ex_iPTgzrvO7qr3 + :status: draft + + Implementing the same functionality as a function: + + + .. code-block:: rust + + fn add(a: i32, b: i32) -> i32 { + a + b + } + + let sum = add(2, 32); // Clear, type-safe, and debuggable + .. guideline:: Shall not use Function-like Macros :id: gui_WJlWqgIxmE8P :category: mandatory @@ -342,4 +407,4 @@ Macros fn example_function() { // Compliant implementation - } \ No newline at end of file + } From 89fa2a3fa4242023ceaa0b0be3e968e8a77fe882 Mon Sep 17 00:00:00 2001 From: x0rw Date: Wed, 14 May 2025 11:45:38 +0100 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: Pete LeVasseur --- src/coding-guidelines/macros.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coding-guidelines/macros.rst b/src/coding-guidelines/macros.rst index 93ee52ef..cb5da5f2 100644 --- a/src/coding-guidelines/macros.rst +++ b/src/coding-guidelines/macros.rst @@ -90,7 +90,7 @@ Macros // TODO -.. guideline:: a macro should not be used in place of a function +.. guideline:: A macro should not be used in place of a function :id: gui_2jjWUoF1teOY :category: mandatory :status: draft @@ -108,7 +108,7 @@ Macros :id: rat_M9bp23ctkzQ7 :status: draft - Macros are powerful but they come at the cost of readability, complexity, and maintainability. They obfuscate control flow and type signatures, + Macros are powerful but they come at the cost of readability, complexity, and maintainability. They obfuscate control flow and type signatures. **Debugging Complexity** From 4ed61ab6ee91360eb2aa6a4d35b40dd577dcec27 Mon Sep 17 00:00:00 2001 From: x0rw Date: Fri, 16 May 2025 17:41:21 +0100 Subject: [PATCH 3/4] Update src/coding-guidelines/macros.rst Co-authored-by: Pete LeVasseur --- src/coding-guidelines/macros.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coding-guidelines/macros.rst b/src/coding-guidelines/macros.rst index cb5da5f2..43a32ebd 100644 --- a/src/coding-guidelines/macros.rst +++ b/src/coding-guidelines/macros.rst @@ -145,7 +145,6 @@ Macros :status: draft Implementing the same functionality as a function: - .. code-block:: rust From 63e8d48163f9935850e99d7f959375bcddc45ffb Mon Sep 17 00:00:00 2001 From: x0rw Date: Fri, 16 May 2025 17:42:21 +0100 Subject: [PATCH 4/4] Change compliant/non-compliant example --- src/coding-guidelines/macros.rst | 37 ++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/coding-guidelines/macros.rst b/src/coding-guidelines/macros.rst index 43a32ebd..35430493 100644 --- a/src/coding-guidelines/macros.rst +++ b/src/coding-guidelines/macros.rst @@ -132,27 +132,46 @@ Macros :id: non_compl_ex_TZgk2vG42t2r :status: draft - Using a macro where a simple function would suffice: + Using a macro where a simple function would suffice, leads to hidden mutation: .. code-block:: rust - - macro_rules! add { - ($a:expr, $b:expr) => { $a + $b }; + + macro_rules! increment_and_double { + ($x:expr) => { + { + $x += 1; // mutation is implicit + $x * 2 + } + }; } + let mut num = 5; + let result = increment_and_double!(num); + println!("Result: {}, Num: {}", result, num); + // Result: 12, Num: 6 + + In this example, calling the macro both increments and returns the value in one go—without any clear indication in its “signature” that it mutates its argument. As a result, num is changed behind the scenes, which can surprise readers and make debugging more difficult. + .. compliant_example:: :id: compl_ex_iPTgzrvO7qr3 :status: draft - Implementing the same functionality as a function: - + The same functionality, implemented as a function with explicit borrowing: + .. code-block:: rust - fn add(a: i32, b: i32) -> i32 { - a + b + fn increment_and_double(x: &mut i32) -> i32 { + *x += 1; // mutation is explicit + *x * 2 } + let mut num = 5; + let result = increment_and_double(&mut num); + println!("Result: {}, Num: {}", result, num); + // Result: 12, Num: 6 - let sum = add(2, 32); // Clear, type-safe, and debuggable + The function version makes the mutation and borrowing explicit in its signature, improving readability, safety, and debuggability. + + .. guideline:: Shall not use Function-like Macros :id: gui_WJlWqgIxmE8P