Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Graydon Hoare on Rust 1.0.0-alpha (graydon2.dreamwidth.org)
89 points by steveklabnik on Jan 24, 2015 | hide | past | favorite | 27 comments


"Rust has grown to be a remarkably collective project: 559 contributors!"

The rust project is the best run open source project I've seen. The culture, automation and communication is awesome.


That's not my perception. For example, the language and standard library freeze is in 3 weeks (the beta release), and yet they are doing a complete redesign of the std::io module: http://discuss.rust-lang.org/t/psa-io-old-io/1403

There's no way they could gather enough experience with the new API within three weeks in order to commit to it for the next decades of Rust.


You are right that there are risks in making big changes at this stage, but my understanding is that the I/O redesign is not as radical as it would seem, and it resolves long-standing issues.

There has been a lot of churn recently it's true - more than ever - but much of this activity is directly motivated by making these kinds of commitments. The changes you are seeing now are the culmination of years of iteration and we really do think we're on the home stretch. If we can live with these APIs for a release cycle or two we'll have a good deal of confidence.

Frankly it would be better not to cut it this close, but Rust 1.0 is going to be a great foundation that we can commit to. There will be mistakes and misdesigns that we will have to live with but that is true for all languages.

I too think that the culture and community of the Rust project is special and am always heartened to hear others agree. Whatever happens with Rust 1.0 it's going to be something for a lot of people to be proud of.


The renaming to "old_io" is purely to make updating code clearer easier -- it does not indicate a massive redesign.

The final API will actually be quite similar to the existing std::io in most respects. It fixes a few longstanding complaints, reorganizes the modules into a flatter hierarchy, and renames to match conventions elsewhere. These are tweaks and polish, not a complete rethink.

Where there are deeper changes (e.g. introducing OsString), we are codifying trends that are already in place (e.g. the env_as_bytes function).

In general, the changes to IO also all make the design more conservative, following traditional APIs more closely, exposing fewer high-level abstractions, and removing questionable APIs.

For most applications, updating will just be a matter of updating names and imports.


>There's no way they could gather enough experience with the new API within three weeks in order to commit to it for the next decades of Rust.

It's a module, not core syntax/semantics, so they don't have to commit to anything about it for the "next decades of Rust".

Besides it's not like they're gonna do a "complete redesign" in 3 weeks from scrach and knowing nothing about how it's gonna come out.

They're gonna redesign it based on what they've known for years to be its pain points, and with all the experience they've had using the previous api.


The discussion about reforming IO has been going on since December 12: https://github.com/rust-lang/rfcs/pull/517

Furthermore, consider that many programming languages have gotten popular post-1.0, so if anything, they had _even less_ experience than we will have.


And still they have enough experience with existing API to accept it is much worse in long term perspective. With new API at least most annoying things will be solved.


I'm not criticizing the API overhaul, I'm criticizing the unrealistic release schedule rush.


Rust has been in development for over five years. It is, objectively, one of the least rushed major open source projects ever. What may look like rushed decisions, such as I/O stability, are actually the culmination of months of RFC process and years of experience before that.


That's a legitimate criticism. As of yesterday, how to convert "String" to "&str" still wasn't nailed down. (".as_string(), or "&[]"?). Things are far better than they were a month ago, though.

After using Rust a little, I get the feeling that the borrow system, the big innovation, is in good shape. The type system is clunky. Take a look at this date/time module:

https://github.com/lifthrasiir/rust-chrono/blob/master/src/d...

Note that none of this code does any computation. It's all field access and update. It's not a multi-calendar system with time zone tables. This is the code for stuffing the year into a Datetime object:

    #[inline]
    fn with_year(&self, year: i32) -> Option<DateTime<Off>> {
        self.local().with_year(year).and_then(|datetime|
        self.offset.from_local_datetime(&datetime).single())
    }
This has generics and a closure, neither of which ought to be necessary. Despite all this generality, there are two copies of this code, one for time-zone aware and one for "naive" timestamps. The author of that module has misgivings about the structure. He writes "This is a bit verbose due to Rust's lack of function and method overloading, but in turn we get a rich combination of initialization methods." This is the best of the three date-time modules available; the other two won't even compile with the current build system and are abandoned.

There are some neat ideas in the type system. One is that if you have something which might not be able to return a value, you have it return an "Option<T>" value, which can have a value of type T, or a value of None. Sorting out what was returned requires a "match" statement, which is rather verbose. It's an elegant way to avoid null pointers and error codes, but it's quite verbose in practice. The business with "and_then" and the closure above is to deal with that problem. See "Option Monads in Rust" ("http://www.hoverbear.org/2014/08/12/option-monads-in-rust/") for an explanation.

In Python, situations like this raise a ValueError exception. In Go, situations like this return (uselessvalue, err). The Rust solution is formally correct, but complex and verbose for what it's doing.

I'm worried that all this verbiage will turn new users off. It turns me off, and I have a background in formal systems.


Some of your criticisms are good and valid, but

> Sorting out what was returned requires a "match" statement, which is rather verbose

is particularly low quality: even the code you quote above demonstrates (or at least hints) that this isn't true. `Option` provides a variety of combinator methods that avoid the need for manual pattern matching: http://doc.rust-lang.org/nightly/std/option/enum.Option.html

Also, this isn't extremely true either:

> Note that none of this code does any computation. It's all field access and update

Doesn't accessing fields and updating them count as computation? Anyway, nothing in date/time handling is just field access and updating; time libraries are hard (and I'm very happy that lifthrasiir is tackling it, their libraries are always high-quality and diligently written). There's actual "real" computation happening, you can see this looking at the underlying implementation: https://github.com/lifthrasiir/rust-chrono/blob/cf5e2f322fbc... (and tracking those functions calls through inside that module).


There was no need for a closure in the above code; it could have been written with "if let" and a couple of extra lines. The use of "and_then" was to avoid writing that extra code.

I've never once regretted the fact that Rust has no null pointers, which is basically what you're criticizing. Null pointers cause so many bugs that the minor effort saved by having them is in no way worth it.


The design decisions behind Rust are good ones. The ones that matter here are:

1. Null references are undesirable. Good decision. 2. Exceptions are undesirable. Arguable, but not bad. 3. There should be some way to optionally return a null result from a function. Reasonable enough. 4. Functional composition is good, allowing one to write h(g(f(a))) Standard in most languages. 5. Object method syntax is good, allowing one to write a.f().g().h() to compose functions. Standard in most languages. 6. Types are determined at compile time. Required for speed. 7. Function overloading based on parameter types is bad.

There are unintended consequences of these decisions. The Option<T> wrapper type can have a value of None, which is not a type to which methods can be applied. So, for functions that return Option, the common "a.f().g().h()" form won't work. Workarounds are available, but they're either wordy ("'if let' and a couple of extra lines") or obscure (using a closure as shown in the example).

It may be better in such cases to avoid an object-oriented style, and do composition as "h(g(f(a)))". If all the functions take Option inputs and return Option outputs, None can be passed through the chain of functions by having each function return None if the input is None. Because Rust doesn't support function overloading for plain functions, the names have to be unique within the namespace, so you can't have both "f(a: f64) => Option<f64>" and "f(a: Option<f64>) => Option<f64>". Although some hack with macros/metaprogramming might allow that, by creating wrappers to pass through None.

This is getting to be a lot of machinery to do something that comes up frequently.


> The Option<T> wrapper type can have a value of None, which is not a type to which methods can be applied.

This is not true. I'd like you to the playpen, but it would get autokilled

    let x: Option<i32> = None;
    x.is_some();
h, f, and g in your examples would need to accept an Option as their arguments, but currently don't. That's why you can't chain them together.


> Sorting out what was returned requires a "match" statement

You can unwrap it if you want an error. Or use a helper function if there is a default to provide. But it explicitly makes clear that the function can fail, and leaves error handling to the consumer. The Python version gives no indication that something may go wrong.


> Sorting out what was returned requires a "match" statement, which is rather verbose. It's an elegant way to avoid null pointers and error codes, but it's quite verbose in practice.

I'd be surprised if you hadn't thought of this but have you thought about wrapping up the error handling logic in some macros?


Yes, there's a workaround, but that's not the point. So many layers of cruft in the early days of a language is worrisome. It took C++ two decades to dig itself into a hole this deep.

Is this the price of not having exceptions?


I disagree that it's cruft; to me null pointers feel like cruft, because they silently blow up when you dereference them. The code above handles the None case explicitly, which is what any systems code should be doing.


No, this is the price of choosing to make it impossible to not explicitly check an error, even if your going to ignore any potential error, at least you know it's impossible to accidentally write code which blindly forgets about the potential error cases.


> Is this the price of not having exceptions?

I guess this is a matter of opinion but are you familiar with the school of thought (to which I belong) that claims that exceptions are worse than goto's? (And before anyone comes in to say that goto's are okay, I'm well aware that they make a lot of sense for certain things and I do use them for those things but they should not be the main flow control operation).


XKCD on gotos: http://xkcd.com/292/


It doesn't seem as healthy to me, or as interesting a language, as it was before Hoare was unceremoniously booted from the project.


Didn't he step down voluntarily?[1] Or is there some back story to this that is not general knowledge?

[1] https://mail.mozilla.org/pipermail/rust-dev/2013-August/0054...


To the best of my knowledge, it is not even rumored that he was booted from the project (and there are certainly people who would have a lot to gain from spreading such rumors).


Thank you, kind words really mean a lot to me.


A lovely sentiment, and one that I do share. If we (programmers/developers) are to regard ourselves as professionals then we must plan/design for the fact that we will not be around forever. (In the sense that we may not be associated with a project forever, but also that we will eventually/accidentally/intentionally die at some point. If you really care about your profession, you'll hopefully plan for that.)


Congrats! and thank you to the entire Rust team.




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

Search: