How to spell a signature for iter().chain()?

I tried for about half-hour to do this, but failed. Please, help.

There is a very simple example from Iterator docs:

let a1 = [1, 2, 3];
let a2 = [4, 5, 6];

let mut iter = a1.iter().chain(a2.iter());

Now, let's raise the problem a bit: write a signature for the function which takes a1, a2 and returns iter:

fn foo(a1: [i32;3], a2:[i32;3]) -> MisteriousType {
    a1.iter().chain(a2.iter())
}

fn main(){
  let a1 = [1, 2, 3];
  let a2 = [4, 5, 6];

  let mut iter = foo(a1,a2);
}

What is Mysterious Type? My every next attempt brings me more and more rustc wrath on me, as error messages become really, really complicated...

You need two components, the slice iterator std::slice::Iter and the chain adaptor std::iter::Chain.

The type will be Chain<Iter<'a, i32>, Iter<'a, i32>>; but foo needs to be rewritten so that it takes references to arrays, not the arrays themselves (otherwise we can't return an iterator that borrows from the input).

As written, you can't!

Here's why: the iter method is implemented on [T], borrows the slice and returns std::iter::Slice<'a, T>, so your function would return something like

use std::slice::Iter;
use std::iter::Chain;
fn foo(a1: [i32;3], a2:[i32;3]) -> Chain<Iter<'a, i32>, Iter<'b, i32>> {
    a1.iter().chain(a2.iter())
}

But there is no way to assign 'a or 'b for this to be valid. (You are trying to return a dangling iterator)

The fix is as @bluss states

use std::slice::Iter;
use std::iter::Chain;
fn foo<'a>(a1: &'a [i32;3], a2: &'a [i32;3]) -> Chain<Iter<'a, i32>, Iter<'a, i32>> {
    a1.iter().chain(a2.iter())
}

Which could be relaxed to

use std::slice::Iter;
use std::iter::Chain;
fn foo<'a>(a1: &'a [i32], a2: &'a [i32]) -> Chain<Iter<'a, i32>, Iter<'a, i32>> {
    a1.iter().chain(a2.iter())
}

Another option is to return an opaque type

fn foo<'a>(a1: &'a [i32], a2: &'a [i32]) -> impl Iterator<Item = &'a i32> {
    a1.iter().chain(a2.iter())
}
5 Likes

You can ask the compiler:

fn foo(a1: [i32;3], a2:[i32;3]) -> _ {
    a1.iter().chain(a2.iter())
}
  Compiling playground v0.0.1 (/playground)
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
 --> src/lib.rs:1:36
  |
1 | fn foo(a1: [i32;3], a2:[i32;3]) -> _ {
  |                                    ^
  |                                    |
  |                                    not allowed in type signatures
  |                                    help: replace with the correct return type: `std::iter::Chain<std::slice::Iter<i32>, std::slice::Iter<i32>>`

error: aborting due to previous error

note the:

correct return type: `std::iter::Chain<std::slice::Iter<i32>, std::slice::Iter<i32>>`

Following this path will of course not directly lead to success in this case, as previous answers have explained, but it can get you to more insight:

Applying the replacement suggested by the compiler:

fn foo(a1: [i32;3], a2:[i32;3]) -> std::iter::Chain<std::slice::Iter<i32>, std::slice::Iter<i32>> {
    a1.iter().chain(a2.iter())
}

gives

   Compiling playground v0.0.1 (/playground)
error[E0106]: missing lifetime specifier
 --> src/lib.rs:1:70
  |
1 | fn foo(a1: [i32;3], a2:[i32;3]) -> std::iter::Chain<std::slice::Iter<i32>, std::slice::Iter<i32>> {
  |                                                                      ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
help: consider using the `'static` lifetime
  |
1 | fn foo(a1: [i32;3], a2:[i32;3]) -> std::iter::Chain<std::slice::Iter<'static, i32>, std::slice::Iter<i32>> {
  |                                                                      ^^^^^^^^

error[E0106]: missing lifetime specifier
 --> src/lib.rs:1:93
  |
1 | fn foo(a1: [i32;3], a2:[i32;3]) -> std::iter::Chain<std::slice::Iter<i32>, std::slice::Iter<i32>> {
  |                                                                                             ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
help: consider using the `'static` lifetime
  |
1 | fn foo(a1: [i32;3], a2:[i32;3]) -> std::iter::Chain<std::slice::Iter<i32>, std::slice::Iter<'static, i32>> {
  |                                                                                             ^^^^^^^^

error: aborting due to 2 previous errors

Then, adding the suggested 'static lifetimes:

fn foo(a1: [i32;3], a2:[i32;3]) -> std::iter::Chain<std::slice::Iter<'static, i32>, std::slice::Iter<'static, i32>> {
    a1.iter().chain(a2.iter())
}

finally explains the issue:

   Compiling playground v0.0.1 (/playground)
error[E0515]: cannot return value referencing function parameter `a1`
 --> src/lib.rs:2:5
  |
2 |     a1.iter().chain(a2.iter())
  |     --^^^^^^^^^^^^^^^^^^^^^^^^
  |     |
  |     returns a value referencing data owned by the current function
  |     `a1` is borrowed here

error[E0515]: cannot return value referencing function parameter `a2`
 --> src/lib.rs:2:5
  |
2 |     a1.iter().chain(a2.iter())
  |     ^^^^^^^^^^^^^^^^--^^^^^^^^
  |     |               |
  |     |               `a2` is borrowed here
  |     returns a value referencing data owned by the current function

error: aborting due to 2 previous errors



Once you realize that the iterators borrow their arguments, you can change the implementation of the function and re-try:

// now taking references as parameter, so that you don’t need to reference
// data owned by `foo` anymore
fn foo(a1: &[i32;3], a2: &[i32;3]) -> _ {
    a1.iter().chain(a2.iter())
}
   Compiling playground v0.0.1 (/playground)
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
 --> src/lib.rs:1:39
  |
1 | fn foo(a1: &[i32;3], a2: &[i32;3]) -> _ {
  |                                       ^
  |                                       |
  |                                       not allowed in type signatures
  |                                       help: replace with the correct return type: `std::iter::Chain<std::slice::Iter<i32>, std::slice::Iter<i32>>`

error: aborting due to previous error

Still suggesting std::iter::Chain<std::slice::Iter<i32>, std::slice::Iter<i32>>:

fn foo(a1: &[i32;3], a2: &[i32;3]) -> std::iter::Chain<std::slice::Iter<i32>, std::slice::Iter<i32>> {
    a1.iter().chain(a2.iter())
}
   Compiling playground v0.0.1 (/playground)
error[E0106]: missing lifetime specifier
 --> src/lib.rs:1:73
  |
1 | fn foo(a1: &[i32;3], a2: &[i32;3]) -> std::iter::Chain<std::slice::Iter<i32>, std::slice::Iter<i32>> {
  |            --------      --------                                       ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a1` or `a2`
help: consider introducing a named lifetime parameter
  |
1 | fn foo<'a>(a1: &'a [i32;3], a2: &'a [i32;3]) -> std::iter::Chain<std::slice::Iter<'a, i32>, std::slice::Iter<i32>> {
  |       ^^^^     ^^^^^^^^^^^      ^^^^^^^^^^^                                       ^^^

error[E0106]: missing lifetime specifier
 --> src/lib.rs:1:96
  |
1 | fn foo(a1: &[i32;3], a2: &[i32;3]) -> std::iter::Chain<std::slice::Iter<i32>, std::slice::Iter<i32>> {
  |            --------      --------                                                              ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a1` or `a2`
help: consider introducing a named lifetime parameter
  |
1 | fn foo<'a>(a1: &'a [i32;3], a2: &'a [i32;3]) -> std::iter::Chain<std::slice::Iter<i32>, std::slice::Iter<'a, i32>> {
  |       ^^^^     ^^^^^^^^^^^      ^^^^^^^^^^^                                                              ^^^

error: aborting due to 2 previous errors

Okay, then this compiles:

fn foo<'a>(a1: &'a [i32;3], a2: &'a [i32;3]) -> std::iter::Chain<std::slice::Iter<'a, i32>, std::slice::Iter<'a, i32>> {
    a1.iter().chain(a2.iter())
}
5 Likes