Kir Chou
Kir Chou

Reputation: 3080

Type casting for Option type

I'm newbie in Rust from Python. I believe it's a basic question, but I am too new to find the answer by keywords like Type Casting Option.

In Python, to make the type checker know return type is not Optional[int] + int, we can address assert logic to enforce the type checker know x will never be None after line assert.

from typing import Optional


def add_one(x: Optional[int] = None) -> int:
    if x is None:
        x = 0
    assert x is not None
    return x + 1


if __name__ == '__main__':
    add_one(0)    # 1
    add_one()     # 1
    add_one(999)  # 1000

In Rust, assuming the interface is same, how do achieve the same thing? Namely, how to make compiler know the type of x is not Option anymore?

fn add_one(mut x: Option<i32>) -> i32 {
    if x == None {
        x = Some(0);
    }
    return x + 1;
}

fn main() {
    add_one(Some(0));
    add_one(None);
    add_one(Some(999));
}

Here's the error message:

error[E0369]: binary operation `+` cannot be applied to type `std::option::Option<i32>`
 --> tmp.rs:5:14
  |
5 |     return x + 1;
  |            - ^ - {integer}
  |            |
  |            std::option::Option<i32>
  |
  = note: an implementation of `std::ops::Add` might be missing for `std::option::Option<i32>`

Note that I've tried things like adding another variable with type i32 (let y: i32 = x;), but it didn't work either with following message.

error[E0308]: mismatched types
 --> tmp.rs:5:22
  |
5 |     let y: i32 = x;
  |                  ^ expected i32, found enum `std::option::Option`
  |
  = note: expected type `i32`
             found type `std::option::Option<i32>`

Upvotes: 4

Views: 6111

Answers (2)

Masklinn
Masklinn

Reputation: 42207

Namely, how to make compiler know the type of x is not Option anymore?

Rust's type checker doesn't have to be compatible with somewhat dodgy usage patterns, so the way to do that is to redefine x as not an option anymore e.g.:

fn add_one(mut x: Option<i32>) -> i32 {
    let x = if let Some(v) = x {
        v
    } else {
        0
    };
    return x + 1;
}

or the uglier and less efficient (but maybe closer to Python):

fn add_one(mut x: Option<i32>) -> i32 {
    let x = if x == None {
        0
    } else {
        x.unwrap()
    };
    return x + 1;
}

Note that you don't have to shadow x, so you could just as well let y instead. It would probably be cleaner here.

But as Boiethios pointed out, Rust provides utilities for this sort of use-cases e.g. unwrap_or, map_or, ...

Upvotes: 2

Boiethios
Boiethios

Reputation: 42739

Use unwrap_or:

fn add_one(x: Option<i32>) -> i32 {
    x.unwrap_or(0) + 1
}

fn main() {
    assert_eq!(1, add_one(Some(0)));
    assert_eq!(1, add_one(None));
    assert_eq!(1000, add_one(Some(999)));
}

Upvotes: 8

Related Questions