The author takes a very pragmatic, rather than theoretic, approach to defining "functional programming." And it's the one I subscribe to: if we get more reliable software by calling programming patterns that provide many of the benefits of functional programming "functional programming," it is perhaps a moral obligation to loosen the definition.
I believe the best definition of a functional programming language is a language that uses lambda calculus as its evaluation model.
In that sense, we can include languages like Erlang and Ocaml that are not purely functional (not functional in the mathematical sense), but are expression based in their form of computation.
We can also exclude languages Python and JavaScript, which technically allows functions as first order objects, but are decidedly not expression based (statements and control flow do not produce values).
As for Rust, it is primarily expression based, and even things that seem like statements, such as return, secretly produce a value that is part of an expression. Like Erlang, it allows for sequenced expressions with multiple expression statements in a single function body, which is odd, but necessary if the goal is to sidestep purity. It also has a let syntax, but this is different from Haskell's let which is simply a way of introducing a sub-expression to be applied in a primary expression. One could use the Rust let in this way, but it can also be used for statement based programming, such as let x = x + 1;
Therefore, since it is primarily expression based, I would say that Rust is functional. Stylistically it can look very imperative, but so can Haskell, as the article points out.
If you remove stuff that uses UnsafeCell, and remove syscalls, you have a purely functional programming language. You don't really have mutability unless you're using something like RefCell or Mutex. You can program your way around any use of mutable references, or recast their use in a form that avoids them.
I don't see the author's point in section "Move semantics". The second let statements for `total` is just shadowing the first. Nothing fancy going on here.
I guess move semantics are a thing here, i.e. the function call "consumes" the variable. My suggestion to the author would be to show an example that better illustrates this, e.g. a case that causes a compiler error.
All that said, the main way in which Rust falls short is tail call optimization for recursive algorithms. Sadly, it's not likely to happen any time soon due to real technical challenges: https://github.com/rust-lang/rfcs/pull/1888#issuecomment-360... ... and also the fact that WebAssembly does not support tail calls either: https://github.com/WebAssembly/tail-call/blob/master/proposa...