techmore.in

Rust - Return Types

In Rust, the return type of a function specifies the type of value that the function will return. The return type is indicated after the -> symbol in the function signature. Rust supports a variety of return types, including primitive types, complex types, and custom types.

1. Returning Primitive Types

A function can return basic data types like integers, floating-point numbers, booleans, and more.

Examples:

rust
fn add(a: i32, b: i32) -> i32 { a + b // No semicolon to return this expression's result } fn main() { let result = add(5, 3); println!("Result: {}", result); // Output: Result: 8 }

2. Returning Compound Types

Rust functions can return compound types such as tuples, arrays, or structs.

Returning Tuples:

rust
fn swap(a: i32, b: i32) -> (i32, i32) { (b, a) } fn main() { let result = swap(1, 2); println!("Swapped: {:?}", result); // Output: Swapped: (2, 1) }

Returning Structs:

rust
struct Point { x: i32, y: i32, } fn create_point(x: i32, y: i32) -> Point { Point { x, y } } fn main() { let p = create_point(10, 20); println!("Point: ({}, {})", p.x, p.y); // Output: Point: (10, 20) }

3. Returning References

Functions can return references to values. When doing so, you need to manage lifetimes to ensure the returned reference is valid.

Example:

rust
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str { if s1.len() > s2.len() { s1 } else { s2 } } fn main() { let string1 = String::from("long string"); let string2 = "short"; let result = longest(&string1, &string2); println!("The longest string is {}", result); // Output: The longest string is long string }

4. Returning Option and Result Types

Rust provides the Option and Result enums for returning values that might be optional or involve error handling.

Returning Option:

rust
fn divide(a: f64, b: f64) -> Option<f64> { if b == 0.0 { None } else { Some(a / b) } } fn main() { match divide(4.0, 2.0) { Some(result) => println!("Result: {}", result), None => println!("Cannot divide by zero"), } }

Returning Result:

rust
fn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err(String::from("Cannot divide by zero")) } else { Ok(a / b) } } fn main() { match divide(4.0, 0.0) { Ok(result) => println!("Result: {}", result), Err(e) => println!("Error: {}", e), } }

5. Returning impl Trait

Rust allows functions to return types that implement a specific trait using the impl Trait syntax. This is useful for returning iterators or types that implement certain traits.

Example:

rust
fn make_adder(x: i32) -> impl Fn(i32) -> i32 { move |y| x + y } fn main() { let add_five = make_adder(5); println!("5 + 3 = {}", add_five(3)); // Output: 5 + 3 = 8 }

6. Returning () (Unit Type)

When a function doesn't return a value, it implicitly returns the unit type ().

Example:

rust
fn greet() { println!("Hello, world!"); // Implicitly returns () } fn main() { greet(); }

7. Early Returns Using return

Rust allows early returns using the return keyword, which can be useful in certain scenarios like error handling.

Example:

rust
fn is_even(n: i32) -> bool { if n % 2 == 0 { return true; // Early return } false } fn main() { println!("Is 4 even? {}", is_even(4)); // Output: Is 4 even? true }

Summary

  • Primitive types: Functions can return basic types like i32, f64, etc.
  • Compound types: Functions can return tuples, arrays, structs, etc.
  • References: Functions can return references with appropriate lifetimes.
  • Option/Result types: Use Option for optional values and Result for error handling.
  • impl Trait: Return types that implement specific traits.
  • Unit type (): Indicates no return value.
  • Early return: Use return to exit a function early.

Understanding return types in Rust helps you design more flexible and robust functions, especially when dealing with complex data and error handling.