fn zins_calc(input: f32, zins: f32) -> f32 {
input - (input * zins)
}
fn tax_calc(input: f32, years: u8) -> f32 {
let years = years - 1;
tax_calc(
zins_calc(
input,
if input >= 1.0 * 10.0_f32.powf(6.0) {
0.12
} else {
0.05
},
),
years,
)
}
That's what cargo clippy has to say about it:
warning: function cannot return without recursing
--> src/main.rs:39:1
|
39 | fn tax_calc(input: f32, years: u8) -> f32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
...
42 | / tax_calc(
43 | | zins_calc(
44 | | input,
45 | | if input >= 1.0 * 10.0_f32.powf(6.0) {
... |
51 | | years,
52 | | )
| |_____- recursive call site
|
= help: a `loop` may express intention better if this is on purpose
= note: `#[warn(unconditional_recursion)]` on by default
I mean... I know that this is recursion. That was my intention. What I don't know is why the code linter has problems with it. However, the code linter doesn't care about the following little trick:
In terms of functionality, it's the same method with a little encapsulation through the years value. It's still recursion. Why doesn't the linter care about that, yet get angry with the other example?
Nitpick: unconditional_recursion is a Rustc lint, not a Clippy lint.
That being said, the lint is raised because there is no exit condition, unless you consider the panic when let years = years - 1; overflows to be your exit condition. I.e. tax_calc recurses indefinitely without ever returning back to the caller. This is basically an endless loop but with recursion instead of loop { ... }, which is what this lint warns against.
I noticed that too when I ran it. Unfortunately, the compiler didn’t. Yeah, OK. That didn’t happen with the iterator-based solution I was using before.
I've fixed it:
Even so, that doesn’t really explain why the compiler no longer recognises recursion when I resolve the whole thing to a [u8;1] using a map(self, f: FNOnce(f32) -> f32).
On the other hand, Clippy should detect an integer underflow. So I’ve properly tricked the linter in my example. clippy::inverted_saturating_sub
That lint for recursion isn't particularly smart. It can't see through stuff like closures. In general, lints will miss stuff sometimes. If it was possible to reliably detect something, it would be built into the language, not be a lint.
Lints are a form of static code analysis. Static code analysis is performed on a syntactic level on the AST. If you obfuscate your code enough, the patterns that a lint looks for in the syntax tree can no longer be found.
Basically, I agree with you. If you want to deliberately confuse the linter, you can. Personally, I found it simply too easy in my particular case. Partly because my coding style tends to reflect the way I think. I’m not a fan of `let` statements.
The more unconventional and idiosyncratic your coding style, the less the compiler (or clippy) is able to help you in general, except in some cases pointing out that the code is unidiomatic. That said, rustc and clippy are of course open source, so as they say, "patches welcome".