News for hackers. Stuff that matters.
Re:Announcing Rust 1.22 by drewm1980 on Thursday November 23, 2017 @01:57AM (#15763334)
I have no idea what that ? operator does from the summary or staring at the usage example... Was more boilerplate necessary in the example before?
Re:Announcing Rust 1.22 by masklinn on Thursday November 23, 2017 @06:58AM (#15764475)
It's a shortcut for "early-returning" in error cases

> Was more boilerplate necessary in the example before?

Somewhat yes, the original use case is Result types (and the try! macro):

    let v = func()?;
expands to

    let v = match func() {
        Ok(v) => v,
        Err(e) => return Err(From::from(e))
    };
that is it "unwraps" a successful result, and barring one it directly returns from the current function (having possibly converted the error). Before 1.22, doing that with an Option would be:

    let v = match func() {
        Some(v) => v,
        None => return None
    };
or

    let v = if let Some(v) { v } else { return None };
after 1.22, this becomes

    let v = func()?;
as well.

edit: note that this is quite different from the behaviour of the ? operator in languages like C# or Swift where it compounds into "null-safe" operators, the most famous being the null-safe navigation operator "?."[0], these are closer to the monadic bind, which in Rust is called "and_then".

[0] https://en.wikipedia.org/wiki/Safe_navigation_operator

Re:Announcing Rust 1.22 by wscott on Thursday November 23, 2017 @06:17AM (#15764349)
"About a year ago, in Rust 1.13, we introduced the ? operator"

And that includes a link to the 1.13 announcement with a detailed explanation. https://blog.rust-lang.org/2016/11/10/Rust-1.13.html

Re:Announcing Rust 1.22 by viraptor on Thursday November 23, 2017 @02:05AM (#15763368)
It skips a block you'd need otherwise. Without it, you'd have to do something like:

    fn try_option_some() -> Option<u8> {
      if let val = Some(1) {
        Some(val)
      } else {
        None
      }
    }
It's an early exit if the expression before is None/Error
Re:Announcing Rust 1.22 by Sharlin on Thursday November 23, 2017 @03:49AM (#15763801)
There should really be a more real-world example, the current code (and your alternative version) is just redundant and equivalent to

  fn try_option_some() -> Option<u8> {
      Some(1)
  }
Re:Announcing Rust 1.22 by lower on Thursday November 23, 2017 @04:19AM (#15763892)
Suppose you have two functions

    fn f() -> Result<A, E>
    fn g() -> Result<B, E>
Then you can write

     fn h() -> Result<(A,B), E> {
        let x = f()?;
        let y = g()?;
        Ok((x, y))
     }
rather than having to do something like

    fn h() -> Result<(A,B), E> {
       match f() {
           Ok(x)  => {
             match g() {
               Ok(y)  => Ok((x,y)),
               Err(e) => Err(e)
             } 
           },
           Err(e) => Err(e),
       } 
     }
(written from memory, please excuse syntax errors)

What's new is that this also works with Option<A> now.

Re:Announcing Rust 1.22 by zZorgz on Thursday November 23, 2017 @07:33AM (#15764606)
As others mentioned, it looks equivalent to using the try! macro (which I'm not sure this is supposed to replace?)

Another sane alternative here is using and_then, map, etc.

Writing from memory too:

  fn h() -> Result<(A, B), E> {
    f().and_then(|x| g().map(|y| (x, y)))
  }
..which seems to be a different style of control flow to choose from. On one hand, I suppose one may avoid using and_then with code dealing with side effects. On another hand, ? seems restricted to being used inside functions that have to return a a single type (like Result) they operate on.
Re:Announcing Rust 1.22 by steveklabnik on Thursday November 23, 2017 @07:40AM (#15764626)
? is explicitly a replacement for try!, yes.

> functions that have to return a single type

Well, all functions have to return a single type, though that type may be a composite type, like a tuple.

Less pedantically, `?` lets you (well, will let you) unwrap values and convert their error cases between each other. So once the next round of stabilization happens, if you have a function that returns Results, you can mix ? on Options and Results in the body, and vice versa. And it can be extended for other types too. Basically, it's an early return "protocol" if you will.

Re:Announcing Rust 1.22 by zZorgz on Thursday November 23, 2017 @07:51AM (#15764676)
By single type, I meant to hint not being able to mix ? on Options and Results in the body, but good to know that's soon possible! Though you still can't, eg, use `?` in main()
Re:Announcing Rust 1.22 by kibwen on Thursday November 23, 2017 @10:00AM (#15765425)
Tracking issue for using it in main: https://github.com/rust-lang/rust/issues/43301
Re:Announcing Rust 1.22 by mmstick on Tuesday November 28, 2017 @02:26PM (#15800532)
You can mix them if you take your Options and convert them into Results with the Option::ok_or(E) method.

    do_this(x).ok_or(CustomError::ThisError)?;
Re:Announcing Rust 1.22 by zZorgz on Thursday November 23, 2017 @08:09AM (#15764774)
Also regarding an "early return protocol", I'd also be curious for continue, break, any ! type expression. For one of the Rust projects I worked on, I wrote an unwrap macro on Option that takes a secondary ! argument.
Re:Announcing Rust 1.22 by steveklabnik on Thursday November 23, 2017 @08:05AM (#15764747)
That's coming as well!
Re:Announcing Rust 1.22 by zZorgz on Thursday November 23, 2017 @01:29PM (#15766925)
Interesting. Looking at the proposal it seems specific to main() and not any void returning function, by automatically picking an exit status. I suppose I worry the construct has a lack of control (log diagnostics, exit status, cleaning, etc) when bailing and can’t handle eg continue, break too.
Re:Announcing Rust 1.22 by steveklabnik on Thursday November 23, 2017 @02:27PM (#15767274)
exit status and cleaning should be just fine, logging or something else would need to not do it, yeah. continue and break would be weird outside of loops.
Re:Announcing Rust 1.22 by Manishearth on Thursday November 23, 2017 @07:16AM (#15764532)
Your example is confusing, too; you've removed the code after the try? so it's not equivalent to the example in the blog, and because it's a single thing there's no early return anymore, thoroughly obfuscating what it is that try did :|
Re:Announcing Rust 1.22 by irishsultan on Thursday November 23, 2017 @11:47AM (#15766202)
Unless I'm missing something it's exactly equivalent (and likely to compile to the same code), after all both check whether there is a value to extract and immediately return None if there isn't, both return Some(val) if there is a value to extract.

If you added extra code to manipulate things then that single if would still be enough, just place everything that happens after the extraction inside the success branch of the if statement.

Re:Announcing Rust 1.22 by Manishearth on Thursday November 23, 2017 @04:52PM (#15768026)
You are missing something, it won't compile at all.

You're missing a Some(val) at the end. Now you could remove the `if let val` as well to make it compile, but then that obfuscates what the ? operator was doing there.

Re:Announcing Rust 1.22 by irishsultan on Thursday November 23, 2017 @05:25PM (#15768215)
The error seems to be that `if let val = Some(1)` should be `if let Some(val) = Some(1)` (i.e. the pattern match should have extracted val, which is what the ? operator does as well).

I think it would have been much clearer to have 1 function which took a parameter of type Option<u8>, then did something with it after extracting with ?, then call it twice, once with Some(1), once with None. In addition I'd probably do something with val (e.g. add 1). And to prevent easy conversion to the if statement you could make the early return explicit by doing the extraction somewhere in a loop (so that you can't get at it with nested if statements).

Re:Announcing Rust 1.22 by Manishearth on Thursday November 23, 2017 @05:34PM (#15768261)
No, that, _and_ that if let ... {} doesn't evaluate to anything; your function doesn't evaluate to anything even though it has a stated return type of Option.

I mean, this isn't hard to check, try compiling it.

I was saying it's misleading not only because it's wrong, but also because even if something is equivalent if you're trying to show how a feature works you want equivalent code that cleanly maps to the original; and isn't further reduced.

Re:Announcing Rust 1.22 by irishsultan on Thursday November 23, 2017 @05:52PM (#15768355)
It does evaluate to something, try this if you don't believe me.

  fn try_option_some(param: Option<u8>) -> Option<u8> {
    if let Some(val) = param {
      Some(val + 1)
    } else {
      None
    }
  }

  fn main() {
    print!("{:?}\n", try_option_some(None));
    print!("{:?}\n", try_option_some(Some(2)));
  }
Re:Announcing Rust 1.22 by Manishearth on Thursday November 23, 2017 @10:48PM (#15769289)
My bad, I think I read this as let val = if .... by accident. Oops
Re:Announcing Rust 1.22 by viraptor on Thursday November 23, 2017 @09:11PM (#15769031)
https://rustbyexample.com/flow_control/if_else.html

> if-else conditionals are expressions, and, all branches must return the same type.

Re:Announcing Rust 1.22 by Manishearth on Thursday November 23, 2017 @10:49PM (#15769290)
Yeah; I misread the original code
Re:Announcing Rust 1.22 by viraptor on Thursday November 23, 2017 @09:10PM (#15769029)
Sorry, it was written on a mobile. Yes, it should be "if let Some (val)" but I can't edit anymore.
Re:Announcing Rust 1.22 by masklinn on Thursday November 23, 2017 @06:54AM (#15764468)
Technically it also applies a conversion operator to the "error" value (a no-op for the Option -> Option case, but it can be otherwise for Option -> Result or Result<T, E> -> Result<T, F>
Re:Announcing Rust 1.22 by cesarb on Thursday November 23, 2017 @05:33AM (#15764178)
The ? operator used to be the try! macro: https://doc.rust-lang.org/book/first-edition/error-handling....

It's used for shortcut error handling: when for instance you receive an error result from opening a file, you usually want to also return with an error. It was used so often that the ? operator was introduced as a shortcut. Now the operator is being extended to Option<T>, which works similar to Result<T, ()>.

Re:Announcing Rust 1.22 by drewm1980 on Thursday November 23, 2017 @02:04AM (#15763366)
Ok so I guess it's a trinary operator with two arguments combined that is able to use Option and Result for the test... I hope autoformatters keep stuff like that on one line!
Re:Announcing Rust 1.22 by GolDDranks on Thursday November 23, 2017 @03:05AM (#15763605)
It's an error handling operator that does an early exit in case of None and unwraps the value in case of Some. It used to work only with Result: early exit when Err, unwrapping the value when Ok.
Re:Announcing Rust 1.22 by Manishearth on Thursday November 23, 2017 @07:17AM (#15764538)
Nah, in that example there should be a `return` before the `None` -- because it's the only block in the function it doesn't matter.

But it's not a ternary operator, it's unary.

let val = foo.bar()?; will call `bar()`, return if it is None, and if it is `Some(foo)`, set `val` to `foo`.