Factorial number in Rust for learning purposes
Bugs and types

You should think some about what types you want to have as input and output for factorial. Negative numbers won't ever work as input, and the output won't be negative, so you should consider using an unsigned integer, like u64. Even doing this, though, there's one input that leads to an infinite loop. See if you can find it and fix the function!

Cleaning up the error handling

match io::stdin().read_line(&amp;mut user_input) {
    Ok(_) =&gt; (),
    Err(_e) =&gt; {
        println!("error: {}", _e);
        continue;
    }
};


A variable with a leading underscore tells readers (and linters) that the variable isn't going to be used. Since we use it (with the println!), we should just call it e.

This error message should probably be in stderr, rather than stdout. This can be done just by changing it to eprintln! instead.

When only one branch of a match actually does something, if let is both easier to write and understand. So in all, I'd change this part to

if let Err(e) = io::stdin().read_line(&amp;mut user_input) {
    eprintln!("error: {}", e);
    continue;
}




let num: i64 = match user_input.trim().parse::&lt;i64&gt;() {
    Ok(n) =&gt; n,
    Err(_e) =&gt; {
        trim_newline(&amp;mut user_input); // remove trailing newline
        println!("'{}' is not a valid number, full error: {}", user_input, _e);
        continue;
    },
};


Same thing with _e vs e. You're using the variable, so it shouldn't start with an underscore. Also the same thing with eprintln!. Errors should be printed to stderr.

Since both branches of the match are used, if let is less useful. However, you might check out the answers to this question for some ideas for making this look better. For a single use like this, you're probably fine, but if you do this "unwrap or continue" behavior a lot, it can help to encapsulate it.

The i64 annotations are unnecessary. The compiler has enough information to conclude that the type of num is i64 since it's fed into factorial. If you want to keep an annotation for explicitness, that's fine, but only one of num: i64 and parse::&lt;i64&gt; is needed for that.

trim_newline seems to be unnecessary. You're parsing user_input.trim() anyway, so just show that to the user if there's a parsing error.

let num = match user_input.trim().parse() {
    Ok(n) =&gt; n,
    Err(e) =&gt; {
        eprintln!("'{}' is not a valid number, full error: {}", user_input.trim(), e);
        continue;
    }
};


Using standard tools for formatting and linting

One thing that goes a long way with making your code more readable and idiomatic is running cargo fmt. You may need to install it with rustup component add rustfmt.

Clippy is another standard tool for catching errors and making your code more idiomatic. It can similarly be installed with rustup using rustup component add clippy. Running it (with cargo clippy) on your program, we get the warning

warning: unneeded `return` statement
 --&gt; src/main.rs:4:5
  |
4 | /     return if num == 1 {
5 | |         1
6 | |     }else{
7 | |         num * factorial(num - 1)
8 | |     }
  | |_____^
  |
  = note: `#[warn(clippy::needless_return)]` on by default
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
help: remove `return`
  |
4 |     if num == 1 {
5 |         1
6 |     }else{
7 |         num * factorial(num - 1)
8 |     }
  |


Since the return statement is the last thing in the function, it's not needed since the last expression in the block is always implicitly returned. You can simply remove the return.

Some advice on overflows

The factorial function grows superexponentially, so any fixed-width integer type is going to quickly overflow. Even switching to u128 only gets you up to an input of 34. The way I see it, you have two choices. Either


Embrace this. Return an Option (or better yet, a Result) to indicate that an overflow occurred. Using something like u64::checked_mul would be helpful for that.
Avoid overflow completely (barring memory limits) by using some sort of BigInt. The crate num_bigint has an implementation with everything you'd need here, including parsing, printing, multiplying and subtracting. You should be able to replace i64 with BigUint (or BigInt) with only a few conversions from integer literals.