Rust track

Lesson 03 · Part 1: Basics

Primitive Types in Depth

Scalar and compound types, tuples and fixed-size arrays

Part 1: Basics4 min readFree lesson

Rust's type system is strict. Integers can also specify the number of bits from i8 to i128, and one character of a string contains the entire Unicode code point in one char. In this episode, we look at scalar types (integer, real number, bool, char) and complex types (tuple, array).

Learning Objectives

  • The number of bits and presence or absence of sign of integer type are used separately.
  • Understand the basic operations of f32/f64, bool, and char.
  • Learn the definition, access, and decomposition grammar of tuples and arrays.
  • I know that the “byte length” and “number of characters” of Korean strings are different.

Key concepts

sign bit 8 16 32 (default) 64 128 pointer width
Yes i8 i16 i32 i64 i128 isize
None u8 u16 u32 u64 u128 usize
  • The default integer literal is i32, and the real number is f64.

  • usize / isize is determined according to the pointer width (32/64 bit) and is used for length and index.

  • Panic overflow in debug build, wrapping in release build. If intentional wrapping is needed, use a method such as wrapping_add.

  • f32 (single precision), f64 (double precision, default).

  • There is no automatic conversion of integer/real-number-is, so explicit casting as: let f = i as f64;

  • When comparing floating decimal numbers, the (a - b).abs() < EPS pattern is safer than equality.

3) bool and char

  • bool is two values: true / false.

  • char is a 4-byte value containing a single quote and one Unicode scalar. char with both Korean and emoji.

  • Strings (&str/String) are enclosed in double quotation marks. s.len() is number of bytes, s.chars().count() is number of characters.

  • Tuple (T1, T2, ...): a bunch of different types, fixed length. The indices are .0, .1.

  • Array [T; N]: Fixed-length bundle of the same type. The index is arr[0]. The length is arr.len().

  • If dynamic length is required, go to Vec<T> after the next part.

See as example

//+3%ED%8E%B8+%EC%98%88%EC%A0%9C+1:+%EC%A0%95%EC%88%98%C2%B7%EC%8B%A4%EC%88%98%C2%B7b ool

fn main() {
    //+%EC%A0%95%EC%88%98+%ED%83%80%EC%9E%85%EC%9D%80+i+/+u+++%EB%B9%84%ED%8A%B8+%EC%88 %98:+i8,+i16,+i32(%EA%B8%B0%EB%B3%B8),+i64,+i128,+isize
    //+%EB%B6%80%ED%98%B8+%EC%97%86 %EB%8A%94+%EC%A0%95%EC%88%98%EB%8A%94+u8+~+u128,+usize.
    let signed: i32 = -42;
    let unsigned: u64 = 100_000_000;
    let pointer_size: usize = 1024; //+%EB%B0%B0%EC%97%B4/%EB%B2%A1 %ED%84%B0+%EA%B8%B8%EC%9D%B4+%ED%91%9C%ED%98%84%EC%97%90+%EC%82%AC%EC%9A%A9

    println!("i32  = {signed}");
    println!("u64  = {unsigned}");
    println!("usize= {pointer_size}");

    //+%EC%8B% A4%EC%88%98+%ED%83%80%EC%9E%85%EC%9D%80+f32,+f64(%EA%B8%B0%EB%B3%B8)
    let pi: f64 = 3.141_592_653_589_793;
    let half = 0.5_f32;
    println!("pi   = {pi}");
    println!("half = {half}");

    //+%EC%82%B0%EC%88 %A0+%EC%97%B0%EC%82%B0%EC%9E%90:+++-+*+/+%
    //+%EC%A0%95%EC%88%98+%EB%82%98%EB%88%97%EC% 85%88%EC%9D%80+%EB%B2%84%EB%A6%BC+(%EC%86%8C%EC%88%98%EC%A0%90+%EB%B2%84%EB%A0%A4%EC%A7%90)
    let div = 7 / 2;       // 3
    let rem = 7 % 2;       // 1
    let fdiv = 7.0 / 2.0;  // 3.5
    println!("7/2 = {div}, 7%2 = {rem}, 7.0/2.0 = {fdiv}");

    // bool — true / false
    let on: bool = true;
    let off = !on;
    println!("on = {on}, off = {off}");
}
//Part 3 Example 2: char is a Unicode scalar character (4 bytes)

fn main() {
    let a: char = 'A';
let han: char = 'Han';
    let emoji: char = '🦀';
    let escape: char = '\n';

    println!("ASCII : {a}");
    println!("Hangul: {han}");
    println!("Emoji: {emoji}");
    println!("The newline escape character is also char: {}", escape as u32);

    //char is a single quote, a string (`
    let s: &str = "hello";
    println!("String: {s}, Byte Length: {}", s.len());
    //⚠️ s.len() is the length in “bytes”. One Korean character usually takes 3 bytes.
    println!("Number of characters: {}", s.chars().count());
}
//Part 3 Example 3: Tuples and Arrays

fn main() {
    //Tuple: Fixed-length collection that can group different types
    let point: (i32, i32, &str) = (3, 5, "origin");

    //Index access is `.0`, `.1` ...
    println!("x = {}, y = {}, name = {}", point.0, point.1, point.2);

    //Destructuring
    let (x, y, name) = point;
    println!("Decomposition: {x}, {y}, {name}");

    //Array: Fixed-length collection of the same type [T; N]
    let scores: [u32; 5] = [80, 90, 75, 100, 60];
    println!("First score: {}", scores[0]);
    println!("Number of scores: {}", scores.len());

    //Fill with same value: [value; Number]
    let zeros = [0u8; 4];
    println!("zeros = {:?}", zeros);
}

Common Mistakes

A: No. Rust does not automatically convert between integers. Casting as (x as i64) must be specified where required. Careless narrowing conversions can result in loss of information, so the compiler prevents them.

A. The behavior is different: panic in debug builds and wrapping in release builds. If the intention is wrapping, use an explicit method such as wrapping_add, and if detection is necessary, use checked_add (Option) or overflowing_add (tuple).

A. len() returns UTF-8 number of bytes. One Korean character is usually 3 bytes, so two characters are 6. If you need the number of characters, use chars().count(). (Graphics graphime is more complex, but for beginners, chars() is sufficient.)

A. You need to know at compile time. For collections whose size changes at runtime, use Vec<T>. (Module/Vec will be covered in detail in a later lesson.)

Cleanup

  • iN/uN for integers, f32/f64 for real numbers, and i32 and f64 for basic heuristics.
  • Conversion between types is not automatic, but explicit casting (as).
  • Note that char is one Unicode character and &str.len() is the number of bytes.
  • The tuple is a heterogeneous bundle (T1, T2, ...), and the array is a homogeneous fixed length [T; N].

Try it yourself

Solve the assignment below. The reference solution is shown below.

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/basic_types/Cargo.tomlTOML
[package]
name = "basic_types"
version = "0.1.0"
edition = "2021"
default-run = "basic_types"

[dependencies]
src/basic_types/src/main.rsRust
fn main() {
    println!("This crate has three running examples.");
    println!();
    println!("  cargo run --bin ex01_numbers");
    println!("  cargo run --bin ex02_chars");
    println!("  cargo run --bin ex03_tuple_array");
}

Assignment

  • File name: homework_01/src/main.rs
  • Core concepts: integer/real operation, / and %

Requirements

  • Variables a = 17, b = 5.
  • Prints the quotient and remainder of integer division.
  • Real division results are also output separately (up to two decimal places).

Expected output

17 ÷ 5 = 3, remainder 2
17.0 / 5.0 = 3.40

Hint

  • Integers: a / b, a % b.

  • Mistake: a as f64 / b as f64.

  • Decimal place format: {:.2}.

  • File name: homework_02/src/main.rs

  • Core concepts: array [T; N], iterator sum

Requirements

  • The array let scores = [85, 90, 78, 92, 88, 70]; is given.
  • Prints the total and average (up to two decimal places).

Expected output

Score: [85, 90, 78, 92, 88, 70]
Total: 503
Average: 83.83

Hint

  • scores.iter().sum::<i32>() or let sum: i32 = scores.iter().sum();.
  • The average requires calculation of real numbers, so as f64.
  • The output of the array itself is {:?} (Debug format).

Check the correct answer

After solving it yourself, compare it with the reference solution below.

Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

homework_01/Cargo.tomlTOML
[package]
name = "homework_01"
version = "0.1.0"
edition = "2021"

[dependencies]
homework_01/src/main.rsRust
//Key points:
//- Integer and real numbers are not automatically converted, so `as f64` casting is required.
//- Formatting to 2 decimal places is `{:.2}`.

fn main() {
    let a = 17;
    let b = 5;

    let q = a / b;
    let r = a % b;
    println!("{a} ÷ {b} = {q}, remainder {r}");

    let f = a as f64 / b as f64;
    println!("{:.1} / {:.1} = {:.2}", a as f64, b as f64, f);
}
homework_02/Cargo.tomlTOML
[package]
name = "homework_02"
version = "0.1.0"
edition = "2021"

[dependencies]
homework_02/src/main.rsRust
//Key points:
//- `iter().sum()` adds up all elements of the collection.
//- The result type is ambiguous, so write the type on the left side like `let sum: i32 = ...;`.
//- Average is calculated as a real number — `sum as f64 / len as f64`.

fn main() {
    let scores = [85, 90, 78, 92, 88, 70];

    let sum: i32 = scores.iter().sum();
    let avg = sum as f64 / scores.len() as f64;

    println!("Score: {:?}", scores);
    println!("Total: {sum}");
    println!("Average: {:.2}", avg);
}

Learn this lesson with a mentor

Work through the Rust track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now