Variadic functions - just curious

Hi there,
Just out of curiosity: how do you deal with the classic problem of the function that can receive a variable number of parameters? For example a function that must add a certain (variable) quantity of numbers or concatenate strings, remaining on classic problems ....
thx for your suggestions

Why don't you use a [str] if you need a non-fixed number of strings?

Yes, of course, but my question was just addressed as an "exercise" to solve the problem using the variadic functions (or what you can use in Rust).

Accepting an IntoIterator type[1] is a really flexible way to support variable length input. Users can pass arrays, slices, Vecs, or a custom iterator that calculates each item dynamically depending on how they want to use your function, and what your function requires.

Playground

use std::{borrow::Borrow, ops::Add};

/// This function has one catch: If there's only a single item in the iterator it will return None. I can't think of a good way to get `I::Item` to `O` with just the traits in std. Ideally we could add "zero", but there's not a built in way to get a zero value for the numeric types. Summing over a single element is sort of weird anyway.
fn sum<I, O>(numbers: I) -> Option<O>
where
    I: IntoIterator,
    // Each item should be addable with itself, and have output type O.
    // We could constrain the output types without using O, but it gets ugly fast with fully qualified paths.
    // T::Item isn't necessary here but I included it for completeness.
    I::Item: Add<I::Item, Output = O>,
    // The output needs to be addable with each item the iterator produces, and the output of that addition should be the same type O.
    O: Add<I::Item, Output = O>,
{
    // The first item the iterator produces, stored temporarily so we can add it to the next item.
    let mut first = None;
    let mut output = None;

    for number in numbers {
        if let Some(sum) = output {
            output = Some(sum + number);
            continue;
        }

        if let Some(prev_num) = first {
            output = Some(prev_num + number);
            first = None;
        } else {
            first = Some(number);
        }
    }

    output
}

fn concat<I>(strings: I) -> String
where
    I: IntoIterator,
    I::Item: AsRef<str>,
{
    let mut total = String::new();

    for string in strings {
        total += string.as_ref();
    }

    total
}

fn main() {
    assert_eq!(sum(&[1u8, 2, 3, 4, 5]), Some(15));
    assert_eq!(sum(vec![1u8, 2, 3, 4, 5]), Some(15));
    // Kind of weird, should probably be fixed in an actual implementation.
    assert_eq!(sum([1]), None);

    assert_eq!(concat(["one", "two"]), "onetwo".to_string());
    assert_eq!(concat(&vec!["one", "two"]), "onetwo".to_string());
    assert_eq!(
        concat(["one".to_string(), "two".to_string()]),
        "onetwo".to_string()
    );
    assert_eq!(
        concat(&vec!["one".to_string(), "two".to_string()]),
        "onetwo".to_string()
    );
}

That implementation of sum is pretty wonky, but hopefully it at least demonstrates how iterators can be used to handle "variadic" function parameters in a pretty flexible way


  1. or an Iterator depending on your needs ↩ī¸Ž

1 Like

Interesting ... I need some time to understand how it works...
thx