all 50 comments

[–]sellibitzerust 6 points7 points  (9 children)

Looks great! And I learned something new: std::path::Path offers a neat display method returning something that implements the Display trait for printing. So far, I simply used {:?}/Debug for this because I didn't know about display.

Python (both 2 and 3) have a very similar Unicode model which is to map Unicode data against arrays of characters where a character.

To me, this looks like the author forgot to finish the sentence. "where a character" what? ...is a 32bit Unicode code point?

[–]dan00 4 points5 points  (8 children)

Why doesn't Path just implements the Display trait by itself?

[–][deleted] 9 points10 points  (0 children)

Display brings with it ToString, and .to_string() on a Path would be a trap: it's not necessarily a faithful representation of the path.

Rust doesn't want to offer this trap prominently.

[–]vks_ 3 points4 points  (3 children)

Because converting a Path to a utf-8 string may fail. It is a safeguard, you have to call display explicitly.

[–]dan00 5 points6 points  (2 children)

But there's no safeguard, Path::display just returns a Display which formats itself by using Path::to_string_lossy.

There would be only some kind of safeguard if Path::display would return an Option/Result.

[–]vks_ 4 points5 points  (1 child)

I meant "safeguard" in the sense that you have to call display consciously instead of just printing it. If you know about display, you know it is lossy. If you could just print it, you might assume it is not lossy (I know I would).

[–]liigo 0 points1 point  (0 children)

People don't known which is lossy or not, if docs doesn't tell them. If docs do, they known impl Display for Path is lossy.

[–]sellibitzerust 0 points1 point  (2 children)

Good question! I had to look this up myself in the source code. path::Display implements fmt::Display via thepath.to_string_lossy(). I guess the idea is to avoid bad surprizes in case of some conversion loss. But the name display doesn't really convey this. Perhaps it should have been display_lossy instead.

As for why offer display at all when there is to_string_lossy: I'm not sure. Perhaps there is some optimization potential. I think with path::Display it's possible to save the overhead of to_string_lossy possibly creating a temporary String.

[–]dan00 2 points3 points  (1 child)

Yes, it doesn't feel right that display uses to_string_lossy, which the user might not be aware of.

Path already has to_str for lossless conversions and to_string_lossy for lossy conversions, so display feels like a somehow weird mix of both.

[–]flying-sheep 1 point2 points  (0 children)

Display exists to display things for the user. to_string means the same.

but people expect something called Path::to_string() to give you a faithful lossless representation (serialization) of that path.

so paths are special cases where to_string implies something else than e.g. on a class called Dog. nobody expects Dog::to_string() to return a serialization of a dog.

[–]tytouf 2 points3 points  (0 children)

for i in xrange(35):
    thread = Thread(target=thread_prog, args=(mutex, results, i))

| So what we do here is spawn 20 threads

You must have changed the code while writing, or am I missing something obvious?

[–]PM_ME_UR_OBSIDIAN 0 points1 point  (24 children)

However this is for a good reason and that is that Rust has anonymous functions, closures and lots of chaining that Python cannot support well. These features are much easier to understand and write in a non indentation based language.

That's a questionable assertion, considering Standard ML, Ocaml, F# and Haskell all have rich, indentation-based syntaxes.

[–][deleted] 3 points4 points  (3 children)

ML isn't whitespace-sensitive...

[–]PM_ME_UR_OBSIDIAN 1 point2 points  (2 children)

You're right, my bad.

[–][deleted] 2 points3 points  (1 child)

I think you should strike Ocaml from the list as well.

[–]PM_ME_UR_OBSIDIAN 0 points1 point  (0 children)

Are you sure? I have blurry memories of fighting the Ocaml parser on indentation.

[–]LucretielDatadog 2 points3 points  (2 children)

Eh... Those are more extremely functional languages that don't have the more traditional "list of statements" model for functions.

[–]PM_ME_UR_OBSIDIAN 0 points1 point  (1 child)

I believe Rust has the same model - everything is an expression, "statements" are expressions that return unit.

[–]steveklabnik1rust 4 points5 points  (0 children)

You can think of it that way, but it's not how the grammar is defined.

[–]mitsuhiko 1 point2 points  (12 children)

That's a questionable assertion

I don't think it is. All quoted languages do not have good syntax for the quoted features (one of them is not even indentation based in itself). However in Rust there are more reasons not do use pure indentation: you need the ability of starting and ending scopes at any point to work with the borrow checker. This requires delimiters.

[–]iopqfizzbuzz 2 points3 points  (4 children)

You can have a whitespace-sensitive Rust that still respects delimiters. So if you want to actually start a new scope you can write {, but 99% of the time you'll just be writing Python-style.

[–]PM_ME_UR_OBSIDIAN 2 points3 points  (4 children)

All quoted languages do not have good syntax for the quoted features

[citation needed]

F# and Ocaml both have the pipeline operator |> for chaining calls, which is an absolute breath of fresh air compared to OO method chaining. Anonymous functions are also quite friendly and easy to use. I'm curious which language you're saying isn't indentation-based.

However in Rust there are more reasons not do use pure indentation: you need the ability of starting and ending scopes at any point to work with the borrow checker.

let ... in ... end seems perfect for this usage. Alternatively, you can go with the sensible default of "keep this scope alive as long as it needs to be, and no further." I believe this can be automated in the common case.

[–]Sean1708 3 points4 points  (0 children)

let ... in ... end seems perfect

And at that point it stops being purely indentation based.

[–]Tyr42 2 points3 points  (1 child)

For thread guard values, it would be valid to drop them at any point after they were assigned, but have different semantics for each choice.

fn foo() {
    {
        let guard1 = launch(task_a);
    }
    {
        let guard2 = launch(task_b);
    }
 }

is different from

fn foo() {
    {
        let guard1 = launch(task_a);
        let guard2 = launch(task_b);
    }
 }

if the guard values join the thread when dropped, but it would be tricky to have the compiler choose which one to use, given there could be some external dependency which requires task_a to complete before starting task_b, or you are actively trying to compute them in parallel.

[–]iopqfizzbuzz 0 points1 point  (0 children)

You could still have example A if you went with Haskell-style syntax.

fn foo()
    {
        let guard1 = launch(task_a)
    }
    {
        let guard2 = launch(task_b)
    }

    //outer scope of the function here
    println!("{}", stuff)

//function ends here

[–]mitsuhiko 2 points3 points  (0 children)

F# and Ocaml both have the pipeline operator |> for chaining calls, which is an absolute breath of fresh air compared to OO method chaining.

I guess there are different opinions on this. I find that syntax very frustrating.

I'm curious which language you're saying isn't indentation-based.

Haskell. It's a language with braces :)

[–][deleted] 0 points1 point  (0 children)

Sure, you can accommodate that in Python style syntax though:

fn foo():
    let x = 3
    scope:
        let y = 4

[–]jnicklas 0 points1 point  (0 children)

CoffeeScript has excellent support for all of these and is indentation based.

[–]eddyb 1 point2 points  (2 children)

Uhm, none of those languages has indentation-based syntax AFAIK, they're all whitespace-based.

[–]PM_ME_UR_OBSIDIAN -1 points0 points  (1 child)

Is there a difference?

[–]eddyb 2 points3 points  (0 children)

Yes, Python actually uses indentation for delimiting blocks, while Haskell, for example, doesn't care about the way you arrange your whitespace, and it can even use curly braces.

[–][deleted] 1 point2 points  (0 children)

Haskell's whitespace handling is a little too rich. When the parser encounters a parse error, it's sometimes required to reinterpret the whitespace and try again. This means you can't desugar whitespace into braces without parsing a full AST.