8.17 Exercises

Click to see the list of suggested exercises
  1. Maximum Function Variants

    • Variant 1: Write a function max_i32 that takes two i32 parameters by value and returns the maximum value.

      fn max_i32(a: i32, b: i32) -> i32 {
          if a > b { a } else { b }
      }
      
      fn main() {
          let result = max_i32(3, 7);
          println!("The maximum is {}", result); // Output: The maximum is 7
      }
    • Variant 2: Write a function max_ref that takes references (&i32) to two i32 values and returns a reference (&i32) to the maximum value. Pay attention to lifetimes.

      // The lifetime 'a indicates that the returned reference is tied to the shortest
      // lifetime of the input references 'a' and 'b'.
      fn max_ref<'a>(a: &'a i32, b: &'a i32) -> &'a i32 {
         if a > b { a } else { b }
      }
      
      fn main() {
          let x = 5;
          let y = 10;
          let result_ref = max_ref(&x, &y);
          println!("The maximum reference points to: {}", result_ref); // Output: 10
          // *result_ref is 10. result_ref is valid as long as x and y are.
      }
    • Variant 3: Write a single generic function max_generic that works with any type T that can be compared (PartialOrd) and copied (Copy). Test it with i32 and f64.

      use std::cmp::PartialOrd;
      use std::marker::Copy; // Often implicitly required by usage, good to be explicit
      
      fn max_generic<T: PartialOrd + Copy>(a: T, b: T) -> T {
          if a > b { a } else { b }
      }
      
      fn main() {
          let int_max = max_generic(3, 7);
          let float_max = max_generic(2.5, 1.8);
          println!("The maximum integer is {}", int_max);     // Output: 7
          println!("The maximum float is {}", float_max);     // Output: 2.5
      }
  2. String Concatenation Write a function concat_strings that takes two string slices (&str) as input and returns a newly allocated String containing the concatenation of the two.

    fn concat_strings(s1: &str, s2: &str) -> String {
        let mut result = String::with_capacity(s1.len() + s2.len()); // Pre-allocate cap.
        result.push_str(s1);
        result.push_str(s2);
        result // Return the new owned String
    }
    
    fn main() {
        let greeting = "Hello, ";
        let name = "Rustacean!";
        let combined = concat_strings(greeting, name);
        println!("{}", combined); // Output: Hello, Rustacean!
    }
  3. Distance Calculation Define a function distance that takes two points as tuples (f64, f64) representing (x, y) coordinates, and returns the Euclidean distance between them as an f64. Recall distance = sqrt((x2-x1)^2 + (y2-y1)^2).

    fn distance(p1: (f64, f64), p2: (f64, f64)) -> f64 {
        let dx = p2.0 - p1.0;
        let dy = p2.1 - p1.1;
        (dx.powi(2) + dy.powi(2)).sqrt() // Use powi(2) for squaring, then sqrt()
    }
    
    fn main() {
        let point_a = (0.0, 0.0);
        let point_b = (3.0, 4.0);
        let dist = distance(point_a, point_b);
        println!("Distance between {:?} and {:?} is {}", point_a, point_b, dist); // 5.0
    }
  4. Array Reversal In-Place Write a function reverse_slice that takes a mutable slice of i32 (&mut [i32]) and reverses the order of its elements in place (without creating a new array or vector).

    fn reverse_slice(slice: &mut [i32]) {
        let len = slice.len();
        if len == 0 { return; } // Handle empty slice
        let mid = len / 2;
        for i in 0..mid {
            // Swap element i with element len - 1 - i
            slice.swap(i, len - 1 - i);
        }
    }
    
    fn main() {
        let mut data1 = [1, 2, 3, 4, 5];
        reverse_slice(&mut data1);
        println!("Reversed data1: {:?}", data1); // Output: [5, 4, 3, 2, 1]
    
        let mut data2 = [10, 20, 30, 40];
        reverse_slice(&mut data2);
        println!("Reversed data2: {:?}", data2); // Output: [40, 30, 20, 10]
    
        let mut data3: [i32; 0] = []; // Empty slice
        reverse_slice(&mut data3);
        println!("Reversed empty: {:?}", data3); // Output: []
    }
  5. Find Element in Slice Write a function find_index that takes a slice of i32 (&[i32]) and a target i32 value. It should return Option<usize>, containing Some(index) if the target is found, and None otherwise. Return the index of the first occurrence.

    fn find_index(slice: &[i32], target: i32) -> Option<usize> {
        for (index, &value) in slice.iter().enumerate() {
            if value == target {
                return Some(index); // Found it, return early
            }
        }
        None // Went through the whole slice, not found
    }
    
    fn main() {
        let numbers = [10, 25, 30, 15, 25, 40];
    
        match find_index(&numbers, 30) {
            Some(idx) => println!("Found 30 at index {}", idx),
            // Output: Found 30 at index 2
            None => println!("30 not found"),
        }
    
        match find_index(&numbers, 25) {
            Some(idx) => println!("Found 25 at index {}", idx),
            // Output: Found 25 at index 1 (first occurrence)
            None => println!("25 not found"),
        }
    
        match find_index(&numbers, 99) {
            Some(idx) => println!("Found 99 at index {}", idx),
            None => println!("99 not found"), // Output: 99 not found
        }
    }