Errors Using RUST generic iter().rev Passing Values to a Function

Rust Members,

This works:

fn main() {
   let arr = [1, 2, 3, 4, 5];
   rev_numbers(arr);
 
}
fn rev_numbers(arr:[i64;5]){
  
  for i in arr.iter().rev() {
        println!("{}", i);
}
}

But this code fails when trying to call the generic function from main. Am I passing parameters incorrectly?

fn rev_numbers(arr: &[i64]) -> Option<i64> {
    let arr = vec![1,2,3,4,5];
        for i in arr.iter().rev() {
             println!({}, i);
             
     }
}
    
fn main() {
    rev_numbers(arr);
}

The compiler give out quite a bit -

  Compiling playground v0.0.1 (/playground)
error: format argument must be a string literal
 --> src/main.rs:4:23
  |
4 |              println!({}, i);
  |                       ^^
  |
help: you might be missing a string literal to format with
  |
4 |              println!("{} {}", {}, i);
  |                       ++++++++

error[E0425]: cannot find value `arr` in this scope
  --> src/main.rs:10:17
   |
10 |     rev_numbers(arr);
   |                 ^^^ help: a tuple variant with a similar name exists: `Err`
  --> /rustc/9eb3afe9ebe9c7d2b84b71002d44f4a0edac95e0/library/core/src/result.rs:512:5
   |
   = note: similarly named tuple variant `Err` defined here

error[E0308]: mismatched types
 --> src/main.rs:3:9
  |
1 |   fn rev_numbers(arr: &[i64]) -> Option<i64> {
  |                                  ----------- expected `Option<i64>` because of return type
2 |       let arr = vec![1,2,3,4,5];
3 | /         for i in arr.iter().rev() {
4 | |              println!({}, i);
5 | |              
6 | |      }
  | |______^ expected enum `Option`, found `()`
  |
  = note:   expected enum `Option<i64>`
          found unit type `()`
help: try adding an expression at the end of the block
  |
6 ~      }
7 +         None
  |
6 ~      }
7 +         Some(())
  |

Some errors have detailed explanations: E0308, E0425.
For more information about an error, try `rustc --explain E0308`.

This has nothing to do with genericity (neither of your functions is generic anyway). You literally forgot to quote your string. "{}" as the first parameter of println!() should work.

1 Like

There's quite a few changes you did to the code, and they result in a handful of unrelated and different kinds of errors.

Let’s start off by applying consistent formatting to both code examples and let's re-order the two functions so the order stays the same. (The order in which functions are defined in Rust does not matter, so this change will not change any of the errors we get.)

fn main() {
    let arr = [1, 2, 3, 4, 5];
    rev_numbers(arr);
}

fn rev_numbers(arr: [i64; 5]) {
    for i in arr.iter().rev() {
        println!("{}", i);
    }
}
fn main() {
    rev_numbers(arr);
}

fn rev_numbers(arr: &[i64]) -> Option<i64> {
    let arr = vec![1, 2, 3, 4, 5];
    for i in arr.iter().rev() {
        println!({}, i);
    }
}

Comparing, we can spot that 4 things changed.

  1. the let arr = … line moved from main into ref_numbers
  2. the argument type of rev_numbers changed from [i64; 5] to &[i64]
  3. the function didn't have any return type and now got -> Option<i64> return type added to its signature
  4. the println!("{}", i) expression was changed into println!({}, i)

Now, the last change in println creates the first error; using {} instead of "{}" is simply a syntactical error… perhaps a typo? The error message “format argument must be a string literal” hints that the first thing in the println!(…) call must be a so-called string literal, which is some text between double quotes. (It must also furthermore be a “format string”, see here for more info.)

The second error comes from the first change, the arr variable is no longer a variable defined in main but in rev_numbers. But in rev_numbers, there also is a function argument called arr which will get overridden (aka “shadowed”) by this definition, whereas in main, arr does no longer exist at all. The latter effect, that arr does no longer exist in main, even though it is used in the call to rev_numbers is an error that manifests, as the compiler calls out ”cannot find value `arr` in this scope“.'

Next, the last error message obviously relates to the changed return type, -> Option<i64>, as the error message contains a hint that prominently points there

1 |   fn rev_numbers(arr: &[i64]) -> Option<i64> {
  |                                  ----------- expected `Option<i64>` because of return type

What this is telling us is that the function signature now claims that a value (of type Option<i32>) is returned from rev_numbers, but the function implementation remains unchanged and actually still returns no value. The error message is slightly more technical in that it refers to the type “()”, aka the “unit” type which is a useful type of values that are really no value at all.

Like a number zero that refers to the number of things when there’s no things at all; or the word “nobody” that can form sentences involving a subject and some action, e.g. “nobody dances”, but the meaning is actually that dancing does not happen at all.

Anyways… back to Rust, like our wish to be able to give every situation, even when there’s nothing there, a number, or to give every possibility of who dances, even if dancing doesn’t even happen, a uniform way of talking about it, every Rust function has a return type and returns something, in principle, and functions that don’t really return anything in practice thus get to return a “value”, the type (), so this type is (among perhaps a few other uses we won’t discuss) the return type of functions that don’t return anything. You could more explicitly call out this fact by re-writing the old function signature

fn rev_numbers(arr: [i64; 5]) {

into the identical, yet differently written, signature

fn rev_numbers(arr: [i64; 5]) -> () {

And now, the change was from -> () into -> Option<i32>, without the necessary change to the function implementation to also return a Option<i32> value, it still returns nothing, i.e. implicitly returns a value of type (), and the error message tells you “hey, there’s a problem, () is not the same as Option<i32>”

error[E0308]: mismatched types
 …
  = note:   expected enum `Option<i64>`
          found unit type `()`

For comparison, the code example that @quinedot had given you in this answer did also feature a return value:

fn max_number(numbers: &[i64]) -> Option<i64> {
    let mut large = numbers.get(0)?;
    for number in numbers {
        if large < number {
            large = number
        }
    }
    
    Some(*large)
}

the last line[1] of a function, if it doesn’t end with “;”, is the return value, the effect in the above function is similar to something written more like

fn max_number(numbers: &[i64]) -> Option<i64> {
    let mut large = numbers.get(0)?;
    for number in numbers {
        if large < number {
            large = number
        }
    }
    
    return Some(*large);
}

in other programming languages without such implicit[2] returns.

With this in mind, the suggestion the compiler gave you should make sense; it suggested adding a line with None or Some(…) to actually return a value from the function and fix the error.


Overall, while the compiler output was lengthy, I would suggest you have a deeper look back into these error messages. The compiler error messages for Rust are generally very useful. You can save yourself and others some time with every compiler error messages that you understand yourself. Reading error messages in Rust completely, i.e. every single line of the message, can find all the necessary information to fix the problem, or at least progress with the fix. If this isn’t the case for you yet anyways, try to practice becoming better with error messages and try to relate e.g. my answer in this forum to the error message you got, in order to have a higher change of understanding similar error messages in the future :slight_smile:

For example the error about println! was probably just a typo, and comparing the changes you made in the code, it should have been easy to spot the problem yourself… well, unless you just overlooked the ""s and didn’t know what a “string literal” is. But then perhaps at least you will know in the future.


By the way, there was a 4th change that didn’t result in an error yet, but will if you resolve the other problems: From the list above, the changed function argument type (into &[i64]) is something we did not talk about yet. If you manage to fix the other issues, you might thus run into an error such as

mismatched types

with additional information

expected `&[i64]`, found array `[i64; 5]`

In case that happens, read the whole message, pay attention to hints such as ”help: consider borrowing here: `&arr` “ that might appear, and perhaps compare your code to the one in @quinedot's example code, to find an approach to address the problem.


As a final nit pick: Note that you are using the term “generic function” incorrectly in your question in the context of Rust. The term “generic function” typically refers to functions with generic type arguments[3], which are described in this chapter in the book.

Of course, this usage of the term “generic” is specific to certain programming languages, including Rust, and outside of these contexts, abstracting functionality in a function in and by itself can be described reasonably, to be “generic” in some sense. But you should avoid using the term “generic” like that in the context of Rust, since that would confuse people accustomed to the technical meaning of the term, referring to generic type arguments.


  1. not always exactly one “line”m longer expressions can also span multiple lines and Rust does not care much about line breaks ↩︎

  2. there’s an argument to be had whether “implicit” is the right word for this, as the syntax is arguable rather short, but still explicit(ly not using a semicolon); but I’ll ignore this possible point of contention ↩︎

  3. or lifetime arguments, or const generics ↩︎

6 Likes

Thank you all for comments, new to Rust, made some fixes but am not sure why E0061 presented. Do I need to add an argument in rev_numbers() in main? :slightly_smiling_face:


fn main() {
    rev_numbers();
}

fn rev_numbers(arr: [i64; 5]) -> () {
    let arr = vec![1, 2, 3, 4, 5];
    for i in arr.iter().rev() {
        println!("{}", i);
    }
    
}

Compiling playground v0.0.1 (/playground)
error[E0061]: this function takes 1 argument but 0 arguments were supplied
--> src/main.rs:2:5
|
2 | rev_numbers();
| ^^^^^^^^^^^-- an argument of type [i64; 5] is missing
|
note: function defined here
--> src/main.rs:5:4
|
5 | fn rev_numbers(arr: [i64; 5]) -> () {
| ^^^^^^^^^^^ -------------
help: provide the argument
|
2 | rev_numbers(/* [i64; 5] */);
| ~~~~~~~~~~~~~~~~

For more information about this error, try rustc --explain E0061.
error: could not compile playground due to previous error

Research into error:

C:\RUST>rustc --explain E0061
An invalid number of arguments was passed when calling a function.

Erroneous code example:

fn f(u: i32) {}

f(); // error!

The number of arguments passed to a function must match the number of arguments
specified in the function signature.

For example, a function like:

fn f(a: u16, b: &str) {}

Must always be called with exactly two arguments, e.g., f(2, "test").

You declared the function to take an argument but you called it without arguments. Why did you expect this to work? This is not at all specific to Rust – the overwhelming majority of languages doesn't allow this (failing either at compile time or at runtime).

If you want your function to operate on the vector declared inside it, then just remove the argument from its declaration. If you want it to operate on an array passed as an argument, then actually do pass an argument (and remove the local, hard-coded arr variable from its body).

Thanks H2CO3 + steffahn +rust masters,

This worked fine:

fn main() {
    rev_numbers();
}

fn rev_numbers() -> () {
    let arr = vec![1, 2, 3, 4, 5];
    for i in arr.iter().rev() {
        print!("{}", i);
    }
    
}

I am lastly stuck on the needed argument for rev_numbers in main. Not sure if I need to declare arr differently then add the variable in the rev_members main.

Apologize for circling this seemingly simply example, but it helps me understand. I appreciate your time and save your words for rote review!

fn main() {
    rev_numbers();
}

fn rev_numbers(arr: &mut Vec<i64>) -> &mut Vec<i64> {
    let arr = vec![1, 2, 3, 4, 5];
    for i in arr.iter().rev() {
        print!("{}", i);
    }
    
}

fn rev_numbers(arr: &mut Vec) -> &mut Vec {
| ^^^^^^^^^^^ ------------------
help: provide the argument
|
2 | rev_numbers(/* &mut Vec */);
| ~~~~~~~~~~~~~~~~~~~~~

Best,
David

Yes, you have to declare arr in main and pass it as argument to your rev_numbers function.

Here a version of your code that compiles, hopefully that helps you:

fn main() {
    let mut arr = vec![1, 2, 3, 4, 5];
    rev_numbers(&mut arr);
}

fn rev_numbers(arr: &mut Vec<i64>) -> &mut Vec<i64> {
    for i in arr.iter().rev() {
        print!("{}", i);
    }
    
    arr
}

Playground.

I'd say the most relevant section from the book you can read up on to get more detailed knowledge on how to write functions in Rust is Functions - The Rust Programming Language.

1 Like

If you're not so familiar with how function arguments work, some resources for general concepts of function arguments in Rust and in programming include


To add some of my own words, as a starting point the, idea of function arguments is perhaps demonstrated well by doing some manual “inlining”: E.g. a code example

fn main() {
    let x = 42;
    let s = f(x);
}
fn f(a: i32) -> String {
    let b = 2 * a;
    format!("{a} - {b}")
}

can be understood (via inlining[1]) by copying the code in the function f into the place where the call to f(x) is happening, and connecting the arguments and the return value appropriately:

Roughly:

fn main() {
    let x = 42;
    let s = {
        // define function arguments according to the call `f(x)`
        let a = x;
        // copy of the function body
        let b = 2 * a;
        format!("{a} - {b}")
    };
}

which in turn, if you are unfamiliar with blocks in Rust, can also be re-written to the mostly equivalent

fn main() {
    let x = 42;
    let a = x;
    let b = 2 * a;
    let s = format!("{a} - {b}");
}

(though this transformation might a bit harder in general, once types with destructors get involved, or when visibility of variables is a relevant concern)


This translation only works at all if the number of function arguments matches in the definition “fn f(a: i32) {” and the call “f(x)”.

Also, for further illustration: for example f(x + x + 42) instead of f(x) would result in initializing the argument with let a = x + x + 42; instead of let a = x.


In this context, the code example @jofas gave

fn main() {
    let mut arr = vec![1, 2, 3, 4, 5];
    rev_numbers(&mut arr);
}

fn rev_numbers(arr: &mut Vec<i64>) -> &mut Vec<i64> {
    for i in arr.iter().rev() {
        print!("{}", i);
    }
    
    arr
}

is easier to handle, if we re-name the arr in rev_numbers, so the below code is completely equivalent

fn main() {
    let mut arr = vec![1, 2, 3, 4, 5];
    rev_numbers(&mut arr);
}

fn rev_numbers(arr_reference: &mut Vec<i64>) -> &mut Vec<i64> {
    for i in arr_reference.iter().rev() {
        print!("{}", i);
    }
    
    arr_reference
}

And now, we can inline, and get:

fn main() {
    let mut arr = vec![1, 2, 3, 4, 5];
    {
        let arr_reference = &mut arr;
        for i in arr_reference.iter().rev() {
            print!("{}", i);
        }
    
        arr_reference
    };
}

and removing the block, this would look like

fn main() {
    let mut arr = vec![1, 2, 3, 4, 5];
    let arr_reference = &mut arr;
    for i in arr_reference.iter().rev() {
        print!("{}", i);
    }
    
    arr_reference;
}

The arr_reference; line may be confusing, it’s the result of us ignoring the return value. If that hadn’t happened, e.g. via

fn main() {
    let mut arr = vec![1, 2, 3, 4, 5];
    let result = rev_numbers(&mut arr);
}

then we would have ended up with something like a line

let result = arr_reference;

instead.


Your original erroring code example cannot be translated in this manner since, as noted above, the number of arguments between function definition and function call must match in order to get a reasonable translation in the first place.


  1. this mental model gets you very far, but in the general case, functions can do a little more than this… e.g. recursion, or dynamic function calls cannot be explained this way ↩︎

3 Likes

That's exactly the same problem as before, just with a different type. At this point, I'm frankly not sure what you are trying to accomplish.

Here we go, this is what I was trying to accomplish. Frankly easier in Python, but I really love Rust and will keep learning. Thanks all.

fn main() {
    let arr = [1i64, 2i64, 3i64, 4i64, 5i64];
    let reversed_arr = reverse_array(&arr);
    for i in 0..reversed_arr.len() {
        print!("{} ", reversed_arr[i]);
    }
}

fn reverse_array(arr: &[i64]) -> [i64; 5] {
    let mut result = [0i64; 5];
    for i in 0..arr.len() {
        result[i] = arr[arr.len() - i - 1];
    }
    result
}


Best, Dave

Thanks - this works!!!! John Madden once went to a seminar put on by legendary Green Bay Packer coach Vince Lombardi who spent 8 hours on the offensive end around - Rust is cool!

Best,
Dave

1 Like

You have to pass arguments to functions in Python as well.

Furthermore, the idiomatic (and shorter/easier) solution in both languages is to reverse the iterator over the array directly, rather than allocate a temporary array and use explicit indexes.

Given the following Python code:

arr = [1, 2, 3, 4, 5]
for item in reversed(arr):
    print(item)

the equivalent Rust code would be:

let arr = [1, 2, 3, 4, 5];
for item in arr.into_iter().rev() {
    println!("{}", item);
}

It's not any harder, really.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.