Search code examples
arraysrecursionrust

An array (or vector) growing in a recursive function


I'm a very beginner in Rust and I'm struggling to implement an algorithm.

Here is the "code" (which doesn't compile) I would like to make work in Rust:

fn main() {
    println!(cumulated_evaluation([], 10));
}

fn cumulated_evaluation(arr: [String], depth: u32) -> i64 {
    if 0 == depth {
        return 0;
    } else {
        return cumulated_evaluation(
            <arr with "some string" appended to it>,
            depth - 1
        ) + evaluation_from_external_crate(&arr);
    }
}

arr can be of type Vec<String> in cumulated_evaluation but evaluation_from_external_crate needs a &[String] as input.

My problems:

  • an array's size must be known at compile time
  • if I use Vec<String> for arr's type, how to use evaluation_from_external_crate?

Solution

  • A simple for loop can do this easily. You can use Vec<String>, which can produce a &[String] by calling as_slice or by dereferencing &*.

    fn cumulated_evaluation(mut arr: Vec<String>, depth: u32) -> i64 {
        let mut sum = 0;
        for _ in 0..depth {
            sum += evaluation_from_external_crate(arr.as_slice());
            arr.push("some string".into());
        }
        sum
    }
    

    Now that it's not recursive, perhaps you would rather create arr inside the function.

    fn cumulated_evaluation(depth: u32) -> i64 {
        let mut arr = Vec::with_capacity(depth as usize);
        let mut sum = 0;
        for _ in 0..depth {
            sum += evaluation_from_external_crate(arr.as_slice());
            arr.push("some string".into());
        }
        sum
    }
    

    Or you can create the whole Vec up front and only pass parts of it to evaluation_from_external_crate.

    fn cumulated_evaluation(depth: u32) -> i64 {
        let arr: Vec<String> = (0..depth - 1).map(|_| "some string".into()).collect();
        let mut sum = 0;
        for d in 0..(depth as usize) {
            sum += evaluation_from_external_crate(&arr[0..d]);
        }
        sum
    }
    

    Or you could do the same thing but in a functional style.

    fn cumulated_evaluation(depth: u32) -> i64 {
        let arr: Vec<String> = (0..depth - 1).map(|_| "some string".into()).collect();
        (0..(depth as usize))
            .map(|d| &arr[0..d])
            .map(evaluation_from_external_crate)
            .sum()
    }
    

    You can't pass a plain slice (this is not an array) like arr: [String] as a parameter because it is not sized. Such types can only be made values behind pointers, which is more-or-less what Vec is doing. Rust arrays are denoted like arr: [String; 10] and have a compile-time constant size that cannot change, although you can slice them just like Vec and slices.