Problem overview
The function fibonacci takes an i32, n, and returns the n-th Fibonacci number. The sequence starts with F(0) = 0 and F(1) = 1, and every later number is the sum of the two before it: F(n) = F(n - 1) + F(n - 2). The input is between 0 and 30.
For n = 4 the answer is 3, because F(4) = F(3) + F(2) = 2 + 1. The first values are 0, 1, 1, 2, 3, 5, 8, 13, and fibonacci(10) is 55.
Approach: the definition is already a recursive function
A recursive function solves a problem by calling itself on smaller versions of the same problem. The Fibonacci definition has exactly that shape. To get F(n), you need F(n - 1) and F(n - 2), and each of those needs two smaller values, and so on.
Every recursive function needs two parts:
- A base case that returns an answer directly, without another call. Without it, the calls never stop.
- A recursive case that moves toward the base case. Here
n - 1andn - 2are both smaller thann, so each chain of calls ends at 0 or 1.
The two starting values F(0) = 0 and F(1) = 1 have a convenient property: in both cases the answer equals n. One check, n < 2, covers both.
Step by step
- Write the base case first. If
nis less than 2, returnn. This is an early exit from the middle of the function, so it uses thereturnkeyword. - Write the recursive case last. For any other
n, add the results of the two smaller calls. This sum is the last expression of the function, with noreturnand no semicolon, so it becomes the return value.
Inside an impl block, a function that takes no self is called as Self::fibonacci(...), not as a bare fibonacci(...).
The solution in Rust
impl Solution {
fn fibonacci(n: i32) -> i32 {
if n < 2 {
return n;
}
Self::fibonacci(n - 1) + Self::fibonacci(n - 2)
}
}
The Rust details worth a closer look:
- The last expression is the result. A line without a trailing semicolon is an expression, and the final one in a function body is what the function returns. Writing
Self::fibonacci(n - 1) + Self::fibonacci(n - 2);with a semicolon would turn it into a statement, the function would return()instead of ani32, and the compiler would reject it with a type mismatch. returnis for early exits. The base case needs it because more code follows. At the end of the function the plain expression is the idiomatic form.- Rust never converts between integer types silently.
nis ani32and so is the result, so no casts are needed. The sum could overflow ani32for largen, but for 0 to 30 the biggest value,F(30), is 832040. - Recursion uses the call stack. The depth here is at most
n, which is tiny for 30.
Complexity
Time: exponential. Each call with n >= 2 makes two more calls, and the same subproblems are solved again and again. For example, F(2) is computed from scratch inside both F(3) and F(4). Counting the calls gives 21891 for fibonacci(20) and 2692537 for fibonacci(30). The count grows by a factor of about 1.6 with each step of n, roughly O(1.6^n). That is fine for n up to 30, and the statement says not to worry about it. Storing results that were already computed, or looping from the bottom up, brings it down to O(n).
Space: O(n). Only one chain of calls is active at a time, and the longest chain follows n - 1 repeatedly down to 1, so at most about n calls are on the stack.
Edge cases and common mistakes
- Missing base case. Without the
n < 2check, the calls continue through 0, -1, -2 and never stop, until the stack overflows and the program aborts. - Checking only
n == 0. A base case for 0 alone leavesF(1)callingF(0)andF(-1), and the recursion runs away. Both 0 and 1 need to stop. - Returning 1 for small inputs.
F(0)is 0, so returning 1 for everyn < 2shifts the whole sequence. - Adding a semicolon to the last line. The function would no longer produce a value.
- Forgetting
Self::. Callingfibonacci(n - 1)inside theimplblock fails to compile, because the function belongs toSolution.
Testing
n = 0gives0.n = 1gives1.n = 2gives1.n = 10gives55.n = 30gives832040.
