Let’s dig a little bit in some implementations (in C++ and Rust) of a function that computes the factorial of an integer passed as argument. I will try to make them as similar as possible and will expose the differences between both:
Recursive factorial
This is the simplest and most known factorial implementation:
int factorial(int n)
{
if (n <= 1)
return 1;
return n * factorial(n - 1);
}
Now, the “same” implementation in Rust:
fn factorial(n : i32) -> i32
{
if n <= 1
{
return n;
}
return n * factorial(n - 1);
}
Though similar and probably producing the same binaries, there are very interesting differences to take into account:
- All functions in Rust start with the “
fn
” keyword. In C++ they start with the function return type,void
, orauto
. - In Rust you must specify the return type after
->
. Since C++11 you can do the same if you mark your method as “auto
“. If you do not specify the return type, the function does not return anything (like a C++ void function). - The Rust type “
i32
” refers to a 32-bit integer. In C++ “int
” represents an integer that could have (as far as I know, all current implementations have a 32-bit integer called: “int
“) a 32-bit representation. This could be not true for old platforms, compilers or very small ones where theint
could be 16-bit. Having an integer with well-defined size for all platforms make code portability easier. (C++ also have theint32_t
alias, but is not an actual type). - Rust’s “
if
” discourages the usage of parenthesis in the expression to evaluate. - Rust mandates the “
if
” and “else
” blocks will be enclosed with curly braces.
Non-recursive implementation
C++ version, using “while
“. I am not using “for
” because the C/C++/Java/C# -like “for
” does not exist in Rust.
int nonRecursiveFactorial(int n)
{
int r = 1;
while (n >= 1)
{
r *= n;
n--;
}
return r;
}
And now, the same code in Rust:
fn non_recursive_factorial(mut n : i32) -> i32
{
let mut r = 1;
while n >= 1
{
r *= n;
n -= 1;
}
return r;
}
Again, interesting differences:
- I called “
nonRecursiveFactorial
” my function in C++ and “non_recursive_factorial” my function in Rust. Though I can call my functions whatever I want, the Rust compiler suggests me to use snake_case instead of camelCase. - Notice I marked my argument as “
mut
” and my variabler
“mut
” as well. “mut
” stands for “mutable” and means that the value of that variable can be modified in its lifetime. All variables in Rust are immutable by default (similar to a C++const
variable) and that simple feature removes a lot of concurrency problems. - Again,
while
does not have parenthesis in its expresion. - Notice I am writing
n -= 1;
instead ofn--;
in Rust. Rust does not have “++
” or “--
” operators because their existence would make lifetime management complicated and the code with those operators can be hard to read in some scenarios.
I want to use “for
” anyway
C++ version:
int factorialWithFor(int n)
{
int r = 1;
for (int i = 2; i < n + 1; i++)
r *= i;
return r;
}
Rust version:
fn factorial_with_for(n : i32) -> i32
{
let mut r = 1;
for i in 2..n + 1
{
r *= i;
}
return r;
}
Once more, interesting differences:
- The “
for
” loop in Rust is a range-based-for-loop, similar to the C++11 range-based-for-loop or C# foreach. - The variable
i
inside the loop is mutable, it is declared and lives only in that block. - After the “
in
” Rust keyword, I wrote “2..n+1
“. That is the Rust way of creating a range of values between [2; n + 1[ (so the loop will run untiln
only). - If I would want to have a countdown instead, I could write “
(2..n + 1).rev()
” instead, that would downcount fromn
to2
.
Until now, very nice language indeed.