Description
Fix three small syntax mistakes in a Rust starter project so it compiles and prints the flag.
Extract the archive, `cd fixme1`, and install Rust/Cargo if you don't already have them.
Run `cargo run` to see the compiler complaints that point directly at the broken lines.
wget https://challenge-files.picoctf.net/c_verbal_sleep/3f0e13f541928f420d9c8c96b06d4dbf7b2fa18b15adbd457108e8c80a1f5883/fixme1.tar.gztar -xvf fixme1.tar.gz && cd fixme1sudo apt install cargo -ycargo runSolution
- Step 1Add the missing semicolonLine 5 ends with a bare expression, so rewrite it as `let x = ...;` to silence the first error.
Learn more
Rust is an expression-based language with two distinct statement forms. A statement is terminated by a semicolon and produces no value. An expression without a semicolon at the end of a block becomes that block's return value. In a
letbinding likelet x = some_value, the semicolon is mandatory - omitting it makes the compiler think you're trying to uselet x = ...as an expression, which is not valid syntax.This distinction matters deeply in Rust because the last expression in a function body (without a semicolon) is implicitly returned. A common pattern is:
fn add(a: i32, b: i32) -> i32 { a + b }- noreturnkeyword needed. But if you accidentally add a semicolon aftera + b, the function returns()(the unit type) instead of the integer, causing a type error.Rust's compiler error messages are famously helpful - they point to the exact line, explain what went wrong, and often suggest a fix. Running
cargo check(which type-checks without producing a binary) is faster thancargo buildduring iterative debugging, andcargo clippycatches a broader set of stylistic and correctness issues. - Step 2Fix the return keywordLine 18 uses the shorthand `ret`. Replace it with the full `return` keyword so the function exits cleanly.
Learn more
Unlike C or Java, Rust does not have abbreviations for keywords.
ret,fnalternatives, or other shorthands simply do not exist - the compiler treats unrecognized identifiers as variable names, leading to "cannot find valueretin this scope" errors. Rust's keyword list is strict and unlikely to grow, making the language's grammar stable and predictable.The
returnkeyword causes early exit from a function, analogous to other languages. However, idiomatic Rust prefers the expression-based implicit return for final values -returnis usually only needed for early exits in the middle of a function body (likeif error { return Err(e); }). The?operator goes further, automatically propagatingErrvariants fromResult-returning functions without an explicitreturnstatement.Learning Rust's keywords and grammar rules is worthwhile beyond CTF: Rust is now the second language (after C) approved for Linux kernel contributions, is heavily used in WebAssembly, systems programming, and security tooling. Familiarity with Rust is increasingly valuable for security engineers working on memory-safe rewrites of critical infrastructure.
- Step 3Correct the println! format stringThe final `println!` macro uses `:?` even though it expects `{}`. Swap the placeholder and re-run `cargo run` to print the flag.
Learn more
Rust's
println!is a macro (note the!) that processes format strings at compile time, generating type-safe code. The two most common format specifiers are{}(Display trait - human-readable output) and{:?}(Debug trait - machine-readable, often quoted). Using{:?}on aStringthat hasn't had its Display impl checked causes a compile error because the output format doesn't match what's expected.The Display trait is implemented manually for custom types and defines how they appear to end users. The Debug trait can be derived automatically with
#[derive(Debug)]and is intended for developer-facing output. Many CTF Rust challenges use{:?}where{}is needed because the author mixed up the two, making this a common error to recognize instantly.Beyond these, Rust supports
{:#?}(pretty-printed Debug),{:b}(binary),{:x}(lowercase hex),{:X}(uppercase hex), and{:e}(scientific notation). All are checked at compile time - a format string mismatch is a compiler error, not a runtime panic, which is one of Rust's key safety advantages over C'sprintf.
Flag
picoCTF{4r3_y0u_4_ru$t4c30n_...}
Follow-up challenges build on the same project. Keep Cargo installed if you plan to tackle Rust Fixme 2 and 3.