
Announcing Rust 1.13 - Manishearth
https://blog.rust-lang.org/2016/11/10/Rust-1.13.html
======
Scaevolus
The ? operator looks lovely. It's like the C# null conditional operator, but
for the Result pattern:

    
    
        try!(try!(try!(foo()).bar()).baz())
    

Becomes

    
    
        foo()?.bar()?.baz()?
    

Versus the Go equivalent:

    
    
        a, err := foo()
        if err != nil {
            return nil, err 
        }
        b, err := a.bar()
        if err != nil {
            return nil, err 
        }
        c, err := b.baz()
        if err != nil {
            return nil, err 
        }
        return c

~~~
Animats
Versus the Python equivalent:

    
    
        try:
            foo().bar().baz()
        except Exception as message: 
            return message

~~~
paulddraper
It's true.

Rust doesn't have exceptions, for many reasons. One is non-locality of
exceptions. Even gotos but bounded by a function. Exceptions, however...those
can take you anywhere.

But the other side, as you point out, is that exception many common patterns
are much made more succient.

~~~
Arnavion
>One is non-locality of exceptions. Even gotos but bounded by a function.
Exceptions, however...those can take you anywhere.

I've never understood this argument.

Where does `try!()` take you if there's an error? To the caller. If the caller
also has a `try!()`, where does that take you? To the caller's caller.

Where does `throw new FooException()` take you? To the caller. If the caller
doesn't have a `catch(FooException)`, where does that take you? To the
caller's caller.

Edit: Note to commenters: I'm not arguing about explicit vs implicit returns.
I do agree that explicit returns are nicer to read and reduce the surface area
of a language. My comment is solely for the "Exceptions can take you anywhere"
sentiment.

~~~
yazaddaruvala
"Checked Exceptions" and the Result pattern can be translated back and forth
by a machine (i.e. syntax sugar for each other).

The trick is "Exceptions" are more than just "Checked Exceptions"

~~~
Retra
Checked exceptions probably act more like ad-hoc enums since you've got to
handle each kind of exception that can be thrown, but I'm sure that could be
encoded in nested results too.

~~~
yazaddaruvala
Fair enough, CheckedExceptions are almost always "fat pointers". Meaning they
are all the same size, and can be passed around equivalently.

The Rust equivalent would be something like:

`Result<_, Box<Error>>`

------
Animats
OK, here's some error handling I wrote about a year ago, for an RSS feed
reader. This code calls lots of packages with their own error types. If
anything fails, an error bubbles up. Those had to be packed up into a single
type for Rust's error handling. What's changed that would make this more
concise?

    
    
        pub type FeedResult<T> = Result<T, FeedError>;  // return type, result or error
    
        // A set of errors that can occur handling RSS or Atom feeds.
        pub enum FeedError {
            FeedIoError(io::Error),                         // error at the I/O level
            FeedHTTPError(hyper::Error),                    // error at the HTTP level
            FeedXMLParseError(xml::BuilderError),           // error at the XML level
            FeedDateParseError(chrono::format::ParseError), // error at the date parsing level
            FeedUnknownFeedTypeError,                       // wrong feed type
            FeedFieldError(String),                         // required RSS field missing
            FeedWasHTMLError(String)                        // expected XML, got HTML
        }
        //
        //  Encapsulate errors from each of the lower level error types
        //
        impl convert::From<hyper::Error> for FeedError {
            fn from(err: hyper::Error) -> FeedError {
                FeedError::FeedHTTPError(err)
            }
        }
        impl convert::From<io::Error> for FeedError {
            fn from(err: io::Error) -> FeedError {
                FeedError::FeedIoError(err)
            }
        }
        

(More of those are required, one for each enum value...) (Then we need this.)

    
    
        //   Convert FeedError to text string.
        impl fmt::Display for FeedError {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                match self {
                    &FeedError::FeedIoError(ref xerr) => xerr.fmt(f),   // I/O error
                    &FeedError::FeedHTTPError(ref xerr) => xerr.fmt(f), // HTTP error
                    &FeedError::FeedXMLParseError(ref xerr) => xerr.fmt(f), // XML parse error
                    &FeedError::FeedDateParseError(ref xerr) => xerr.fmt(f), // Date parse error
                    &FeedError::FeedUnknownFeedTypeError => write!(f, "Unknown feed type."),
                    &FeedError::FeedFieldError(ref s) => write!(f, "Required field \"{}\" missing from RSS/Atom feed.", s),
                    &FeedError::FeedWasHTMLError(ref s) => write!(f, "Expected an RSS/ATOM feed but received a web page \"{}\".", s)
                    //_(ref xerr) => xerr.fmt(f) // would be convenient, but not allowed in Rust.
                }
            }
    

This sort of thing is why I like Python's exception hierarchy. If you catch
the higher level errors, you get all the lower level ones, too, without losing
the info about what went wrong.

~~~
Arnavion
[https://crates.io/crates/error-chain](https://crates.io/crates/error-chain)
or [https://crates.io/crates/quick-error](https://crates.io/crates/quick-
error) (which error-chain uses and adds to) provide macros to help with
defining error types.

Edit: Specifically with error-chain, you get:

\- Concise declaration of the enum for each wrapped error type (your first
code block).

\- Automatic impl of From for each wrapped error type (your first code block).

\- A custom Result type (your first code block).

\- Concise way to define description() functions for each enum variant, which
get used for the Display impl (your second block). For "foreign_links" it
would forward to the wrapped error's fmt automatically, so you only need to do
this for the others.

\- Bonuses like backtraces conditional on RUST_BACKTRACE=1

~~~
Animats
How well does that play with packages that don't use it? There seem to be
several such packages.

~~~
dbaupp
It is a macro that just helps you define a type that implements the
appropriate traits so of course it works with the rest of the world. No-one
outside the crate that uses it even needs to notice, as one could just write
out the definition by hand.

~~~
Arnavion
This is correct. Technically the backtrace functionality and ErrorChainIter
implementation come from the error-chain crate (the former as a re-export of
the backtrace crate) so your linker will notice.

------
cjg
Worth noting:

"1.13 contains a serious bug in code generation for ARM targets using hardware
floats (which is most ARM targets). ARM targets in Rust are presently in our
2nd support tier, so this bug was not determined to block the release. Because
1.13 contains a security update, users that must target ARM are encouraged to
use the 1.14 betas, which will soon get a fix for ARM."

------
Perceptes
Congrats to the team and thank you! As always, my Docker image for Rust has
been updated for 1.13:
[https://hub.docker.com/r/jimmycuadra/rust/](https://hub.docker.com/r/jimmycuadra/rust/)

------
amatheus
Rust looks great, I'm not using it but learning and it look fantastic. I have
one question, I guess off-topic: Rust uses LLVM, like Swift, Julia etc. Could
these LLVM languages interoperate better because of this common foundation?
Maybe link using LLVM IR instead of C ABI FFI, or something like that?

~~~
TD-Linux
The LLVM IR doesn't define an ABI - in particular, most of the ABI is already
"burned in" at the point where LLVM IR exists.

------
the_duke
It's astonishing that, in just 6 weeks 155 people contributed. (1.12 was
September 29th)

Rust sure seems to draw a lot of enthusiasm.

~~~
OJFord
They didn't necessarily actually contribute in that time though, presumably
the list is of people who's contributions were merged into the release branch
(or master during that timeframe, or... according to whatever workflow they're
using).

Not to detract from your point though. It's an awesome language, I'm just
learning it but enthusiastically so for sure!

~~~
Manishearth
I think it's the contributions merged in master that are in the 1.13 release
but not 1.12.

However, the timeframe for that is the same -- it is a _different_ period of 6
weeks, but still a 6 week period.

~~~
OJFord
Why is it a 6 week period?

Someone could open a PR today and not see it merged for a year, surely?

~~~
steveklabnik
The Rust release cycle is six weeks.

They could, but this counts people who have landed a patch, not opened a PR.
That many people got a patch into this release.

~~~
kbenson
I think what's going on here is a rather specific misinterpretation on one or
both your parts of what "contributed" means in this context.

If contributed is interpreted to mean "code was merged" not "work was done"
then the period is a fairly static 6 weeks, but there's really no reason to be
astonished about how many people were involved, as it could contain work from
multiple months ago.

If contributed is interpreted to mean "work was done" then the number of
people that contributed in this period compared to some other will have more
meaning.

I think OJFord is working from the second interpretation.

~~~
steveklabnik
Yes, it means "code was merged." (Though it's not a particularly large number
of contributors, the last release had 176, and the release before that 126.)

This is actually something I'm kinda sad about; I want to get better at
representing work in a broader way. For example, get a patch into Cargo, but
not rustc? You're not on this list. I don't like it. Working on it though...

------
Animats
Well, it's concise. The main problem is that the error action is "return" \-
you have to bail out of the entire function.

What happens if you use "?" within a lambda? Does it bail out of just the
lambda, or the entire function? What about nested functions? (Can you have a
named nested function which can access its outer scope? This StackOverflow
post [1] says no, but may be wrong or obsolete.)

If this works with nested block structure, it's more useful. Having a "match"
for Err/Some around a lambda with with lots of "?" clauses would be useful.
That provides a way to get the error from some complicated chain without
leaving the function.

[1] [http://stackoverflow.com/questions/26685666/a-local-
function...](http://stackoverflow.com/questions/26685666/a-local-function-in-
rust)

~~~
steveklabnik

      >  Does it bail out of just the lambda
    

Yes.

    
    
      >  What about nested functions?
    

It returns from the nested function, not the outer function.

    
    
      > a named nested function which can access its outer scope?
    

No, functions don't close over any scopes.

~~~
Animats
That's good. Now there's a clean way to handle errors in a block-structured
fashion.

------
glennsl
Good to see bors[0] getting some well deserved credit. That guy's a beast!

[0] [https://github.com/bors](https://github.com/bors)

~~~
steveklabnik
Bots are people too!

------
stirner
It seems like a fairly common case for a Rust function to encounter multiple
types of error when it runs. My only solution to this is to return a custom
error type, but this means manually matching rather than using something like
try! or ?.

Can someone with more experience than I tell me if there is a simpler
approach?

~~~
stjepang
Yes, there is a simpler approach.

If your function returns Result<T, Box<Error>>, then you can use try! or ?
operator to return any kind of error. It will be automatically boxed and
converted into the generic Box<Error>.

This works for two reasons:

1) try! and ? don't simply return Err(err) on error case as one might think.
They actually return Err(From::from(err)).

2) The standard library has: impl<'a, E: Error + 'a> From<E> for Box<Error +
'a> { ... }

Check out this article, starting from "The From trait":
[http://blog.burntsushi.net/rust-error-
handling/](http://blog.burntsushi.net/rust-error-handling/)

~~~
stirner
Awesome, thanks. Does returning boxed errors have a performance impact?

~~~
sklopi
It should only have an impact when errors actually occur, i think.

------
shmerl
How far along is the effort for efficient code reuse (such as filling the gaps
of lack of inheritance)?

I.e. this:

* [https://github.com/rust-lang/rfcs/issues/349](https://github.com/rust-lang/rfcs/issues/349)

* [https://github.com/rust-lang/rust/issues/31844](https://github.com/rust-lang/rust/issues/31844)

Is it something coming soon, or it's pretty far still?

~~~
steveklabnik
Specialization would only be one piece of the puzzle, and as you can see, it's
not in stable yet.

All of the bits are still too far out to give a good estimate of.

~~~
shmerl
Thanks. Do you expect that to introduce some non backward compatible changes
to language (and for example require a new major version)?

~~~
nercury
Initial work on specialization has already landed on nightly and is backwards
compatible. AFAIK, there are no plans to break that.

~~~
shmerl
That's good!

------
kccqzy
Excuse me if this is a stupid question, but when I downloaded the official Mac
installer, it's not signed by anyone. I thought in this age we've all
recognized that downloaded software should be signed, whether it's using GPG
or Apple's signature mechanism? Is there a reason the Rust team decides not to
sign their binaries?

~~~
steveklabnik
[https://github.com/rust-lang/rust/issues/27694](https://github.com/rust-
lang/rust/issues/27694)

GPG signatures do exist for the tarballs, though, see

[https://static.rust-
lang.org/dist/rustc-1.13.0-src.tar.gz.as...](https://static.rust-
lang.org/dist/rustc-1.13.0-src.tar.gz.asc) for the source, for example, or
[https://static.rust-
lang.org/dist/rustc-1.13.0-x86_64-apple-...](https://static.rust-
lang.org/dist/rustc-1.13.0-x86_64-apple-darwin.tar.gz.asc) for rustc.

[http://rustup.rs/](http://rustup.rs/) will automatically use all of this if
you have it installed and our key imported.

------
randyrand
Can someone explain to me why rust decided not to support pattern matching
with different types?

Suchas:

let x = Bus();

match x{

    
    
       Car => ...
    
       Bus => ...
    
       Person => ...
    

}

You would need to have runtime type information of course, this could be a
compiler flag.

~~~
elihu
Because rust is statically typed, and "x" has to have a concrete type known at
compile time?

Maybe there's a use case for matching trait objects against types to get back
the original type. But, I think you could do the same thing by adding a trait
method that returns Option<T> for some desired type T. If the object wasn't
that type, it could just return None.

~~~
tatterdemalion
And this is actually implemented by the Any trait.

[https://doc.rust-lang.org/std/any/trait.Any.html](https://doc.rust-
lang.org/std/any/trait.Any.html)

------
tmaly
I am thinking about learning Rust sometime in the near future.

I am curious how long it took you to learn and become proficient?

~~~
saheel1511
I'm in no way proficient but I am a TA for an entry level OOP course at an UC.
It took me 2 weeks to read through the major topics in Rust. And another month
to get a good hold by gradually writing bunch of code.

------
sametmax
Isn't that only necessary because rust doesn't have exceptions ?

~~~
Manishearth
? isn't necessary, it's just nice to have.

Rust doesn't want to have exceptions; it prefers return value based error
handling ("monadic error handling", specifically). A lot of people seem to be
under the impression that Rust's error handling model is a way of emulating
exceptions -- it's not; it's a different model, and we don't want to emulate
exceptions. The fact that error propagation is a common task doesn't mean that
Rust's way of providing it is done to emulate exceptions.

\------

In a sense, every part of Rust's error handling model is "necessary because
Rust doesn't have exceptions". Similarly, every part of Java's error handling
model is "necessary because Java makes it hard to do monadic error handling".
Saying "X is only necessary because you don't have Y" when X is an alternative
to Y isn't a very useful statement.

~~~
sametmax
You are just answering to the style of my question, while you perfectly
understood the meaning and avoided to provide the answer.

Basically this feature is here to provide an easy propagation of errors,
something an exception system does for you for free.

"?" exist because the current error handling make it inconvenient to propagate
errors, and is a workaround to make it less of a pain.

It's alright. There is no shame in it. Just say so, don't try to talk your way
out of it.

~~~
Manishearth
> something an exception system does for you for free.

That's the point, it doesn't, you pay for the exception system and the
implicitness that comes with it. Your comment portrays exceptions as the
default that monadic errors are playing catch-up with. I could easily do the
reverse.

> is a workaround to make it less of a pain.

You say workaround, I say feature.

Ultimately, monadic errors and exceptions have tradeoffs.

Like I said, given any tradeoff between feature A and feature B, you can
always say "subfeature X of A exists because we don't have B". It's not a
meaningful thing to say.

I didn't avoid an answer, I genuinely think that your comment has no substance
to it because it pretends that exceptions are a "default" system.

------
hossbeast
Is it "written in rust" ?

~~~
steveklabnik
Yes.

