Start learning free

LESSON 16 OF 414 · VARIABLES

Type Conversion in Rust: to_string, as and parse

Lesson Read the full explanation

In Rust, every value has a type: i32, f64, String, and so on. Sometimes you need to convert a value from one type to another.

Common type conversion functions:

x.to_string()              // converts i32 or f64 → String
s.parse::<i32>().unwrap()  // converts &str → i32
s.parse::<f64>().unwrap()  // converts &str → f64
x as i32                   // casts f64 → i32 (drops decimal)

Here are some examples:

let score: i32 = 42;
let score_str = score.to_string();   // "42"

let weight: f64 = 70.8;
let weight_int = weight as i32;      // 70  (decimal dropped)

let count = "100";
let count_int: i32 = count.parse().unwrap();  // 100

Note that casting an f64 to i32 with as i32 simply drops the decimal - it does not round.


Assignment
Follow the Coding Tutorial and convert each variable to a different type!


Hint
Use .to_string(), as i32 casting, and .parse().unwrap().

STEP 1 OF 3
    Ctrl + Enter to check

    RUST · VARIABLES

    Type Conversion in Rust, explained

    What type conversion is

    Every value in Rust has one type, fixed at compile time: i32, f64, String, &str and so on. Rust almost never converts between types behind your back, so when you need a number as text, text as a number, or a float as an integer, you ask for it explicitly. There are three everyday tools:

    GoalToolExample
    number to textto_string()25.to_string() gives "25"
    number to another numeric typeas9.99 as i32 gives 9
    text to numberparse()"2024".parse::<i32>() gives Ok(2024)

    Number to String with to_string

    to_string() works on any value that can be displayed, which includes all the numeric types. It returns an owned String, a new piece of text that you can keep, change or return from a function.

    let age = 25;
    let age_string = age.to_string();
    println!("{}", age_string.len()); // 2, the text "25" has two characters
    println!("{}", 9.99_f64.to_string()); // 9.99
    

    The original age is untouched: conversion produces a new value and leaves the old one as it was.

    Float to integer with as

    The as keyword converts between numeric types. From a float to an integer it truncates toward zero: it drops the fractional part and never rounds. That means 9.99 as i32 is 9, not 10, and -9.99 as i32 is -9. When you want rounding, call round() on the float first:

    let price: f64 = 9.99;
    println!("{}", price as i32); // 9
    println!("{}", price.round() as i32); // 10
    let neg: f64 = -9.99;
    println!("{}", neg as i32); // -9
    

    Casts with as never fail and never panic, which is also their danger. A float outside the range of the target type is clamped to the nearest limit: 1e20_f64 as i32 is 2147483647, and f64::NAN as i32 is 0. Between integer types, as keeps only the low bits that fit: 3_000_000_000_i64 as i32 is -1294967296 and 300_i32 as u8 is 44. Use as when you know the value fits or when losing information is what you want.

    String to number with parse

    Text can contain anything, so parsing can fail. That is why parse() returns a Result instead of a number: Ok(value) on success and Err(...) on failure.

    println!("{:?}", "2024".parse::<i32>()); // Ok(2024)
    println!("{:?}", "abc".parse::<i32>()); // Err(ParseIntError { kind: InvalidDigit })
    println!("{:?}", "3.5".parse::<f64>()); // Ok(3.5)
    

    unwrap() takes the value out of an Ok, and it panics on an Err, ending the program with an error message. It is fine when the text is known to be valid, such as the literal "2024" in this lesson. For input you don't control, handle the Err case with match or unwrap_or instead.

    Telling parse which type you want

    parse can produce many types, so the compiler must know which one. There are two ways to say it. You can annotate the variable: let n: i32 = s.parse().unwrap();. Or you can use the "turbofish" on the call: s.parse::<i32>().unwrap(). Without either, the code doesn't compile because the target type can't be inferred.

    The exercise, step by step

    The starter code has three variables: age (an integer), price (a float) and year (a string slice). Each step converts one of them.

    1. Text from a number. Call to_string() on age and store the result in age_string.
    2. Integer from a float. Write price as i32 and store it in price_integer. It holds 9, because the decimal part is dropped.
    3. Number from text. Declare year_integer with the annotation i32 and assign year.parse().unwrap(). The annotation tells parse what to produce, and unwrap() extracts the number from the Result.

    The solution in Rust

        let age = 25;
        let price = 9.99;
        let year = "2024";
    
        let age_string = age.to_string();
        let price_integer = price as i32;
        let year_integer: i32 = year.parse().unwrap();
    

    Details worth noticing:

    • age has no annotation, so Rust picks i32 for the integer literal, and price becomes f64 for the decimal literal.
    • Each line creates a new variable. Nothing is converted in place, because a variable keeps its type for its whole life.
    • The annotation on year_integer is what makes the last line compile. Remove : i32 and the compiler cannot tell what parse should return.

    Common mistakes

    • Expecting rounding. as i32 truncates, so 9.99 becomes 9.
    • Parsing text with extra characters. " 42", "3.5" and "" all fail to parse as i32. Only digits with an optional leading sign are accepted.
    • Parsing a value that doesn't fit. "99999999999".parse::<i32>() is an Err (overflow), whereas casting a larger integer with as silently wraps.
    • Calling unwrap() on untrusted input. One bad string panics the whole program.
    • Assigning across types. let s: String = age; is a type mismatch error. Rust requires the explicit to_string().

    Key takeaways

    • Rust converts types only when you ask, and each conversion creates a new value.
    • to_string() turns a displayable value into an owned String.
    • as converts between numeric types: from float to integer it truncates toward zero, clamps out-of-range floats, and wraps between integer types.
    • parse() returns a Result; give it a target type and use unwrap() only on text you trust.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free