Skip to content

Instantly share code, notes, and snippets.

@vladak
Last active December 19, 2025 17:20
Show Gist options
  • Select an option

  • Save vladak/f8f7ac1d48e4011e061ee0e06cabaf01 to your computer and use it in GitHub Desktop.

Select an option

Save vladak/f8f7ac1d48e4011e061ee0e06cabaf01 to your computer and use it in GitHub Desktop.
Rust gotchas

Learning Rust gotchas

That is, learning from C99.

Basics

  • by default variables are constant. To make a variable mutable, it has to have the mut keyword:
    • otherwise it would not compile
fn main() {
    // also, type inferrence
    let mut foo = 'A';
    foo = 'b';
}
  • duplicate variable definitions get marked merely as unused variables:
fn main() {
  let foo = 1;
  let foo = 3;
}
  • The exclamation mark means this is a macro.
    • for printing there is also print! and eprint! and eprintln! (for stderr)
fn main() {
    println!("Hello, world!");
}
  • print macros have interpolation:
fn main() {
  let foo = "bar";
  println!("{}", foo);
  println!("{foo}"); // since some Rust version
}
  • the ability to use expressions within the {} seems to be limited (unlike in Python)
fn main() {
    let signs = "QRYZEPTGMk ";
    println!("{signs.len()}"); // fails to compile
    println!("{}", signs.len());
}
  • no semicolon at the end of a function means the value should be returned
fn main() {
    // there does not have to a semicolon for the last expression/statement in a block/function
    let x = println!("{}", 'A'.is_ascii_hexdigit())
}
  • there is 128-bit integer type:
fn main() {
    let mut foo:u128 = 2 << 68;
    while foo > 0 {
        // also, string interpolation
        println!("{foo}");
        foo = foo / 8;
    }
}
  • the compiler itself detects overflow (unlike C which leads to floating point HW exception):
let a: i8 = -128;
println!("{}", a / -1); // causes compile error: attempt to compute `i8::MIN / -1_i8`, which would overflow

When println!("{}", a / "-1".parse::<i8>().unwrap()); is used, it causes the "attempt to divide with overflow" panic. The unwrap() call takes the actual result of the parse() operation, if there is one, otherwise panics.

The panic happens only when compiling with dev mode according to https://doc.rust-lang.org/stable/book/ch03-02-data-types.html For release builds the overflow happens.

  • accessing a slice beyond outside index results in panic (due to the bound check):
fn main() {
    let signs = " TGMK";
    // also note that slice cannot be used directly on str (&signs[8]) cause of UTF-8
    let letter = &signs.as_bytes()[8];
 }
  • sliced str (string type) has to be dereferenced and casted to get a printable char:
fn main() {
    let signs = "TGMK ";
    let letter = &signs.as_bytes()[0];
    println!("letter '{}'", *letter as char);
}

data types

statically typed language - the types of all variables have to known at compile time.

rust makes a point about data living on the stack vs. on the heap.

arrays

  • fixed size; there is no such thing as VLAs (although there is a crate that implements it however has some unsafe corners)
    • if dynamic array is needed, use the vector (Vec, allocated on the heap)
  • runtime enforces array boundaries - out of bounds access causes panic
  • boundless case: let a = ["foo", "bar"]
  • case of array with type specification: let a: [i32; 5] = [1, 2, 3, 4, 5];
    • no way to specify lower dimension than the number of items in the initializer (so that the unspecified items can be initialized e.g. to 0)
    • also no way to specify dimension without type it seems (in which case the above will become array of u128)
  • initialize the array with repetition of given value using semicolon: let a = [3; 42] (3 values, each equal to 42)

Build

  • rustc is the low-level compiler utility, cargo is the high-level (make/dependencies)

    • cargo run, cargo build, cargo clean
  • there is no macro language per se like in C, however portions of code can be compiled based on flags

# Cargo.toml

[features]
my_feature = []
    #[cfg(feature = "my_feature")]
    {
        println!("count = {count}");
        print!("{foo} ");
    }

target autodiscovery

https://doc.rust-lang.org/cargo/reference/cargo-targets.html#target-auto-discovery

The directory structure allows e.g. for compilation of multiple binaries. src/bin/{foo,bar}.rs will build 2 programs in the target directory after running cargo build.

Used this for my Advent Of Code solutions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment