Hacker News new | past | comments | ask | show | jobs | submit login

    let mut foo = vec!(1, 2, 3);
    for mut iter in 0..foo.len() {
        iter += 1;
        foo[0] += iter;
    }
No unsafe required, I think.



Points for cleverness - you replaced the iterator with a call to len(). Array indices are sometimes thought of as back-door references, because they let you cheat in this manner.

But you didn't quite get away with it. The C++ code increments the value in the array through the iterator, while the above Rust increments the iterator (err, index) itself. So it doesn't produce the same result.

More generally, std::vector in C++ typically implements iterators with just raw pointers. This is quite unsafe, but doesn't impose borrow-checker restrictions on the user. How can such languages be compiled to Rust?


>But you didn't quite get away with it. The C++ code increments the value in the array through the iterator, while the above Rust increments the iterator (err, index) itself. So it doesn't produce the same result.

I have to admit that I don't understand you here. Can you elaborate on the difference between the two programs?


Sure. The C++:

    for (iter = foo.begin(); iter != foo.end(); ++iter) {
        *iter += 1;
        ...
Here `iter` is a pointer. We dereference it and increment the result. This adds 1 to every value in the `foo` vector.

The Rust:

   for mut iter in 0..foo.len() {
        iter += 1;
        ...
Here `iter` is an array index, and `iter += 1` increments that index. The contents of the `foo` vector are not modified.


>Here `iter` is a pointer. We dereference it and increment the result. This adds 1 to every value in the `foo` vector.

Huh? How is `iter` related to foo? When you go `iter += 1` aren't you just incrementing the variable behind iter?

Unless... OK, I'm not very familiar with C or C++, totally newb here. Are you doing pointer arithmetics here, when you increment iter?

But wait, if you are doing pointer arithmetic, I thought you are not supposed to deref the pointer. Otherwise you are doing arithmetics on the variable behind the pointer, not the pointer itself.

Also, with `foo[0] += *iter`, you are just adding iter to the first element of foo, again and again. You are not actually iterating on foo.

I'm confused...


Think of `foo.begin()` as returning a pointer to the first element of the array, or something which acts like it (similar to Rust's `Deref`). Think of `foo.end()` as returning a pointer to a "virtual" element after the last element of the array. Then `++iter` increments this pointer, so it points to the next element of `foo`.

Since `iter` points to an element of `foo`, `* iter` is the element itself. `* iter += 1` adds one to that element. `foo[0] += * iter` takes the current element, adds it to the first element of `foo`, and stores the result as the first element of `foo`.

The catch is that, in Rust terms, you have a mutable reference to an arbitrary element of `foo` (the reference being `iter`), and you are getting a mutable reference to another element of `foo` (the reference being `foo[0]`) at the same time. Even worse, in the first iteration, `iter` points to the first element, so `iter` and `foo[0]` are two mutable references to the same location, which exist at the same time! You can't do that in Rust with references (you can with pointers and `unsafe`, and in this case it happens that there is no data race, but...)

The correct safe Rust translation would be:

    let mut foo = vec!(1, 2, 3);
    for index in 0..foo.len() {
        foo[index] += 1;
        foo[0] += foo[index];
    }
But, for more complicated cases, an equivalent translation won't be as obvious.


What's the advantage of doing pointer arithmetics as opposed to incrementing index variable, like I did?


The advantage of iterators is the abstraction. Collections like maps and sets don't even have indexes.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: