4
votes

This question uses syntax that predates Rust 1.0, but the concepts are universal and many answers have been updated to reflect stable Rust 1.0.

This question is about a very basic thing that a programmer might try while learning Rust.

You couldn't ask a simpler question about the Rust language, I don't think, but I'm a programmer with 30 years experience, and I can't figure it out.

I think it has something to do with int::range and closures.

Here's what I wrote in a very early version of Rust (pre 1.0).

fn main() {
    int::range(0, 100, {|i| 
        io::println(i);
    });
}

This creates lovely error messages that I have no idea how to fix:

hello.rs:3:19: 5:2 error: mismatched types: expected `&fn(int) -> bool` but found `()` (expected fn but found ())
hello.rs:3 int::range(0, 100, {|i| 
hello.rs:4     io::println(i);
hello.rs:5 });

It's funny how it printed out my entire function body, but still I have no idea what that &fn(int) ->b ool thing means. I vaguely suspect that having a body of an iterator's closure not declare any return type is explicitly disallowed in Rust, which confuses me.

2
It's expecting a function that takes an int and returns bool. You're passing a function that takes nothing and returns nothing. It's like a wrong type variable, except the variable is a method.mcalex
So you can't write a for loop in rust that doesn't return anything. I don't need the "side effect" of returning from the closure, but I guess I have no choice?Warren P
umm, don't know about rust in particular, just recognise the syntax. (C# equivalents are Func<object, bool> & Action<object>)mcalex

2 Answers

11
votes

Rust certainly has come a long way! In Rust 1.0, ranges have some syntax sugar start..end, and implement the Iterator trait. Combined together, you can just say:

fn main() {
    for i in 0..100 {
        println!("{}", i);
    }
}

As of Rust 1.26, you can also use inclusive ranges:

fn main() {
    for i in 0..=99 {
        println!("{}", i);
    }
}

See also:

9
votes

Rust 1.0 has changed quite a bit since this answer was posted. The right way to iterate like this today looks like:

fn main() {
    for i in 0..99 {
        println!("{}", i);
    }
}

Original answer:

int::range takes a &fn(int) -> bool, which means a function that takes an int and returns a bool. Your function doesn't return a bool.

The typical way to deal with this is to use the for syntactical construct, which transforms your function from one that returns () into one that returns bool, which allows it to support break and loop.

for int::range(0, 100) |i| {
    io::println(fmt!("%d", i));
}

(note also that, at least in Rust 0.6, the proper syntax for the function is to put the |i| before the {)


The way higher-order iteration functions work in Rust is by using the bool return value to determine if it should continue iterating. A return value of true means to keep iterating, and a return value of false means to stop iterating. The for construct will rewrite the function to translate break and loop into the appropriate return statements. break will be translated into return false and loop into return true. It will also translate return statements into modification to a temporary flag variable from outside the loop (followed by a return false), which is then consulted after the iteration function is complete to determine if it should return from the outer function.