Was there a huge syntax change

I opened first rust website and saw demo code:

fn main() {
    vec::iter([1, 2, 3]) {|i|
        io::println(#fmt("hello, %d", i));
    }
}

AFAIK it is an invalid code nowadays. How was it working then?
What is the syntax trick when I pass lambda right after calling iter?
Is it syntax still working for any use case?

https://web.archive.org/web/20110924054534/http://www.rust-lang.org/

The linked archive page is from 2011. Rust only release its first stable release, version 1.0, in 2015. Since then, the language remains syntactically unchanged, and all old syntax keeps working, with the caveat of some minor syntactic changes that are opt-in via the “editions” mechanisms (so those won’t break your code, and they won’t split the ecosystem, but allow further syntax changes past 1.0).

Before Rust 1.0, as far as I know, the language syntax and libraries used to change regularly, and significantly, so it’s no surprise that this code example is invalid today. Though I’m not actually familiar with the Rust syntax at that point, my best guess as for the equivalent code today would be something like

fn main() {
    for i in vec![1, 2, 3] {
        println!("hello, {i}");
    }
}

though I’m not entirely sure how much of today’s ownership system existed at the time, or whether the language was perhaps relying more on garbage collection and shared values at the time, so a direct translation might be impossible, or leaves room for multiple alternative approaches, depending on how you look at it.

5 Likes

This code is from a time so long ago that iterators, as we know them today, didn't actually exist in Rust. Back then, IIRC, for loops over iterators didn't exist, because you passed the closure to run to the iterator, and it ran it for each value.

Here's my attempt at a direct translation to modern rust:

fn main() {
    <[_]>::iter(&[1, 2, 3]).for_each(|i|
        println!("{}", format!("hello, {}", i))
    )
}

But that's only done to be as node-for-node similar as possible -- it's not the way that code should be written today.


Or, to directly answer the topic: Pre-1.0 Rust had all kinds of things. It's of historical interest only.

What were you hoping to get out of looking at such as old pre-stability Rust page?

5 Likes

By the way, whilst the code example you gave seems to be from Rust 0.2, on from 0.3, I’m able to find online documentation still hosted today under links such as
https://doc.rust-lang.org/0.3/rust.html -- for the reference
https://doc.rust-lang.org/0.3/tutorial.html -- for a tutorial
https://doc.rust-lang.org/0.3/core/ -- the core library
https://doc.rust-lang.org/0.3/std/ -- the standard library
… and possibly more, so you can explore those without needing to use the archive.org mirrors; but it’s a bit hard to find out what links I’m possibly missing, without any “main” page I know of.

These kinds of links seem to work for various versions.

From 0.9 onwards, there also appears to also be a documentation overview page under: https://doc.rust-lang.org/0.9/.

5 Likes

Just out of curiosity. Under the link provided I get the following code

use std;
fn main() {
  let out = std::io::stdout();
  for i in [1, 2, 3] {
    out.write_str(#fmt("hello %d\n", i));
  }
}

Are you sure you posted the right link?

FYI, going a bit newer on the time-line into mid 2012 gives me the code in the original post.

2 Likes

Thanks :smile:

Wow, this is wild. It changes so often in this time. In july 2012 it looks like this

import io::println;

fn main() {
    for [1, 2, 3].each |i| {
        println(#fmt("hello, %d", i));
    }
}
2 Likes

Well, as Stability as a Deliverable | Rust Blog said,

Since the early days of Rust, there have only been two things you could count on: safety, and change. And sometimes not the first one.

6 Likes

2010 Rust was more like Erlang:

http://venge.net/graydon/talks/intro-talk-2.pdf

For pre-1.0 versions of Rust, you can find a lot of information in doc/tutorial.md or src/doc/tutorial.md. See, e.g., closures in 0.2 vs. 0.3. The original closure syntax was copied from Ruby, but it was later changed to no longer require braces surrounding the argument list and body (commit).

The special closure-based for loop syntax (mirroring the do syntax) was documented in doc/tutorial.md until 0.7, where it was moved to doc/tutorial-container.md. Closure-based for loops were finally replaced with iterator-based for loops by 0.8 (PR), using the familiar for x in y {} syntax we know today. (They were called foreach loops for a brief period while the old for loops were still being phased out.) Meanwhile, do syntax was removed by 0.10 (RFC, PR), since it had caused somewhat of a syntax confusion between stack closures and heap closures (which today are simply boxed closures).

1 Like

One post-1.0 syntax change to be aware of is that one used to just write Trait instead of dyn Trait for trait objects. Are you talking about the trait or the type? Completely contextual; is the grammar expecting a type or a trait?

(One should be aware of this when reading RFCs for example, as many foundational RFCs were written before dyn Trait was a thing.)

1 Like

Aham, what is the difference between Trait and dyn Trait?

In that specific context, nothing.

They are different syntaxes for the same thing.

As we are talking about RFCs already, here’s the relevant RFC introducing the “dyn Trait” syntax itself. So feel free to take a look for clarification :wink:

Besides listing reasons and context the relevant part for explaining the simply the meaning of dyn Trait is this section:

Explanation

The functionality of dyn Trait is identical to today's trait object syntax.

Box<Trait> becomes Box<dyn Trait>.

&Trait and &mut Trait become &dyn Trait and &mut dyn Trait.

Check this out

fn main() {
    let v = vec![1, 2, 3];
    v.iter().for_each(|i|
        println!("{}", format!("hello, {}", i))
    )
}

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.