KAIROS CODERS

Your First Rust Program: Understanding Rust Syntax, Functions, Statements and Expressions

user

Rahul

August 24, 2026 at 10:23 PM

View Count: 6

Your First Rust Program

In the previous article, we installed Rust and created our development environment using rustup and Cargo.

Now it's time to actually start programming.

Rust may look unfamiliar at first, especially if you're coming from languages such as JavaScript, Python, Java, PHP, or C++.

But the fundamentals are straightforward once you understand the structure.

In this article, we'll build your foundation by learning:

  • How a Rust program is structured
  • The main function
  • println!
  • Comments
  • Statements
  • Expressions
  • Variables
  • Basic syntax
  • String literals
  • Numbers
  • How Rust executes your code
  • Common beginner mistakes
  • A small practice project

By the end, you should be comfortable reading and writing simple Rust programs.


Your First Rust Program

Let's start with the classic Rust program:

fn main() {
    println!("Hello, Rust!");
}

It looks simple, but there is already a lot to understand.

Let's break it apart.


Understanding fn main()

The first part is:

fn main()

fn is Rust's keyword for defining a function.

main is the name of the function.

So:

fn main()

means:

Define a function called main.

The curly braces contain the function's body:

fn main() {
    // Code goes here
}

For a normal Rust executable, execution begins with the main function.

You can think of it as the starting point of your application.


What Is a Function?

A function is a reusable block of code that performs a specific task.

For example:

fn greet() {
    println!("Hello!");
}

However, simply defining a function doesn't automatically execute it.

You need to call it:

fn greet() {
    println!("Hello!");
}

fn main() {
    greet();
}

Output:

Hello!

This distinction is important:

fn greet() {
    println!("Hello!");
}

defines the function.

While:

greet();

calls the function.


The println! Macro

Our first program contains:

println!("Hello, Rust!");

println! prints text to the terminal and adds a newline after it.

For example:

fn main() {
    println!("Hello");
    println!("Rust");
}

Output:

Hello
Rust

Notice the exclamation mark:

println!

The ! indicates that println! is a macro rather than a normal function.

Don't worry about macros yet.

We'll study them in detail much later in the series.

For now, remember:

println! is commonly used to print information to the terminal.


Printing Multiple Values

You can use placeholders inside println!.

For example:

fn main() {
    let name = "Rahul";

    println!("Hello, {}", name);
}

Output:

Hello, Rahul

The {} is a formatting placeholder.

Rust replaces it with the value supplied after the string.


Multiple Placeholders

You can use multiple placeholders:

fn main() {
    let name = "Rahul";
    let age = 30;

    println!("My name is {} and I am {} years old.", name, age);
}

Output:

My name is Rahul and I am 30 years old.

Rust matches the placeholders with the supplied values.


Modern Rust Formatting

Rust also supports inline formatting using captured variables:

fn main() {
    let name = "Rust";

    println!("Learning {name} is exciting!");
}

Output:

Learning Rust is exciting!

This is a convenient feature of modern Rust.

You'll see both styles throughout Rust codebases.


Comments in Rust

Comments are text written for developers rather than the compiler.

Rust supports two common types of comments.

Single-Line Comments

Use //:

fn main() {
    // Print a greeting
    println!("Hello, Rust!");
}

Everything after // on that line is treated as a comment.


Multi-Line Comments

Rust also supports block comments:

/*
    This is a multi-line comment.
    It can span multiple lines.
*/

fn main() {
    println!("Hello, Rust!");
}

Comments can help explain complicated logic.

However, don't comment every obvious line.

Prefer code that is naturally understandable.


Statements in Rust

A statement performs an action.

For example:

let name = "Rust";

This creates a variable.

Another example:

println!("Hello");

calls a macro.

Many Rust statements end with a semicolon:

let x = 10;
let y = 20;
println!("{}", x + y);

The semicolon is an important part of Rust syntax.

But there is an interesting exception.

Some Rust constructs are expressions, and expressions can return values.

This distinction is one of the most important concepts in Rust.


Expressions in Rust

An expression evaluates to a value.

For example:

5 + 3

is an expression.

It evaluates to:

8

Another example:

{
    10 + 20
}

evaluates to:

30

Rust uses expressions extensively.


Statements vs Expressions

Consider:

let x = 10;

The let declaration is a statement.

But:

10 + 20

is an expression.

A useful way to think about it is:

Statements perform actions; expressions produce values.

This distinction becomes extremely important when working with functions, if, match, and closures.


The Semicolon Can Change Meaning

Consider:

fn add() -> i32 {
    10 + 20
}

The function returns 30.

But if you write:

fn add() -> i32 {
    10 + 20;
}

you have added a semicolon.

The expression is now treated as a statement, and the function no longer implicitly returns 30.

This is one of the first Rust syntax details beginners need to understand.


Implicit Return

Rust allows the final expression of a function to be returned without writing return.

For example:

fn add() -> i32 {
    10 + 20
}

The result is:

30

You can also explicitly return:

fn add() -> i32 {
    return 10 + 20;
}

Both are valid.

Rust code often prefers the expression-based style:

fn add() -> i32 {
    10 + 20
}

Understanding ->

Look at:

fn add() -> i32 {
    10 + 20
}

The part:

-> i32

specifies the function's return type.

It means:

This function returns a 32-bit signed integer.

We'll study Rust's data types in detail in the next few articles.

For now, simply understand that:

fn add() -> i32

declares a function that returns an i32.


Creating Variables

Let's create a variable:

fn main() {
    let name = "Rust";

    println!("{name}");
}

The keyword:

let

creates a variable binding.

Here:

let name = "Rust";

means that name refers to the string "Rust".


Variables Are Immutable by Default

This is one of Rust's important design choices.

Consider:

fn main() {
    let age = 25;

    age = 26;
}

Rust will reject this program.

Why?

Because variables are immutable by default.

If you want to change a variable, explicitly mark it as mutable.


Mutable Variables

Use mut:

fn main() {
    let mut age = 25;

    age = 26;

    println!("{age}");
}

Output:

26

The keyword:

mut

means the variable can be modified.

This explicitness helps Rust prevent accidental changes.


Why Are Variables Immutable by Default?

Imagine a large application with hundreds of variables.

If every variable could be modified at any point, understanding the program could become difficult.

Rust instead encourages:

let value = 100;

when the value should not change.

And:

let mut value = 100;

when modification is intentional.

The programmer makes the decision explicit.


Shadowing

Rust also supports something called shadowing.

For example:

fn main() {
    let number = 10;

    let number = 20;

    println!("{number}");
}

Output:

20

The second let creates a new binding that shadows the first one.

This is different from mutation.


Shadowing vs Mutation

Mutation:

let mut number = 10;
number = 20;

Shadowing:

let number = 10;
let number = 20;

With mutation, you're changing the value associated with an existing mutable binding.

With shadowing, you're creating a new binding with the same name.

This distinction becomes particularly useful when transforming values.

For example:

fn main() {
    let spaces = "   ";

    let spaces = spaces.len();

    println!("{spaces}");
}

The first spaces is a string.

The second spaces is a number.

Rust allows this because the second declaration creates a new binding.


Basic String Literals

A string literal can be written using double quotes:

let language = "Rust";

The value:

"Rust"

is a string literal.

You can print it:

println!("{language}");

Strings become much more interesting when we discuss ownership, borrowing, and the String type.

For now, distinguish between:

"Rust"

and:

String::from("Rust")

They are not the same thing.

We'll explore the difference later.


Numbers

Rust supports several numeric types.

For example:

let age = 30;
let price = 99.99;

Rust can infer many types automatically.

You can also explicitly specify them:

let age: i32 = 30;

Here:

i32

is the type.

We'll dedicate an entire article to Rust's data types, so don't worry about memorizing them yet.


Basic Arithmetic

Rust supports standard arithmetic operators.

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

    println!("Addition: {}", a + b);
    println!("Subtraction: {}", a - b);
    println!("Multiplication: {}", a * b);
    println!("Division: {}", a / b);
    println!("Remainder: {}", a % b);
}

Output:

Addition: 15
Subtraction: 5
Multiplication: 50
Division: 2
Remainder: 0

The % operator gives the remainder.


Rust Is Statically Typed

Rust is a statically typed language.

This means types are checked during compilation.

For example:

let age: i32 = 30;

Rust knows that age is an i32.

You don't always have to explicitly write the type because Rust can often infer it.

For example:

let age = 30;

The compiler can determine an appropriate integer type based on context.

This feature is called type inference.


Type Annotations

You can explicitly specify a type:

let age: i32 = 30;

The general syntax is:

let variable_name: Type = value;

For example:

let score: i32 = 95;
let price: f64 = 99.50;
let active: bool = true;

We'll explore each of these types later.


Creating Your Own Function

Let's create a simple function:

fn greet() {
    println!("Welcome to Rust!");
}

fn main() {
    greet();
}

Execution begins at:

main()

Then main calls:

greet()

The program prints:

Welcome to Rust!

Functions With Parameters

Functions can accept data.

fn greet(name: &str) {
    println!("Hello, {name}!");
}

fn main() {
    greet("Rahul");
}

Output:

Hello, Rahul!

The function parameter is:

name: &str

We'll explain &str properly when we study strings and borrowing.

For now, understand the general structure:

fn function_name(parameter: Type)

Functions With Return Values

A function can return a value.

fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    let result = add(10, 20);

    println!("{result}");
}

Output:

30

The function:

fn add(a: i32, b: i32) -> i32

accepts two integers and returns an integer.


Building a Small Calculator

Let's combine what we've learned.

fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn subtract(a: i32, b: i32) -> i32 {
    a - b
}

fn multiply(a: i32, b: i32) -> i32 {
    a * b
}

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

    println!("Addition: {}", add(a, b));
    println!("Subtraction: {}", subtract(a, b));
    println!("Multiplication: {}", multiply(a, b));
}

Output:

Addition: 25
Subtraction: 15
Multiplication: 100

This tiny program already demonstrates:

  • Functions
  • Parameters
  • Return values
  • Variables
  • Arithmetic
  • Formatting
  • main

Rust Code Blocks

Curly braces define blocks of code.

For example:

fn main() {
    println!("Inside main");
}

The braces define the body of main.

Blocks can also produce values:

let result = {
    let a = 10;
    let b = 20;

    a + b
};

Now:

result

contains:

30

Notice again that the final expression has no semicolon:

a + b

Therefore, it becomes the value of the block.


Rust's Naming Conventions

Rust follows common naming conventions.

Variables and functions generally use:

snake_case

Examples:

let user_name = "Rahul";

fn calculate_total() {
}

Types generally use:

PascalCase

For example:

struct UserAccount {
}

We'll learn structs later.

Following Rust's conventions makes your code look familiar to other Rust developers.


A Complete Beginner Example

Let's create a slightly larger program:

fn calculate_total(price: f64, quantity: f64) -> f64 {
    price * quantity
}

fn main() {
    let product = "Laptop";
    let price = 75000.0;
    let quantity = 2.0;

    let total = calculate_total(price, quantity);

    println!("Product: {product}");
    println!("Price: ₹{price}");
    println!("Quantity: {quantity}");
    println!("Total: ₹{total}");
}

This program demonstrates several fundamental Rust concepts together.


How Rust Executes Your Program

When you run:

cargo run

Cargo starts the build process.

Conceptually:

Rust source code
       ↓
Cargo
       ↓
Rust compiler
       ↓
Machine code
       ↓
Executable
       ↓
Program runs

The compiler performs extensive checks before producing the executable.

This is one of the fundamental differences between writing Rust and working with many interpreted languages.


Compiler Errors Are Your Friend

As you learn Rust, you will make mistakes.

That's normal.

For example:

fn main() {
    let mut number = 10;

    number = "Rust";
}

Rust will reject this because number was initially inferred as an integer, but you're attempting to assign a string to it.

Instead of allowing potentially inconsistent behavior, the compiler catches the problem.

Rust's compiler messages are often detailed enough to help you understand what went wrong.

Learning to read compiler errors is an important Rust skill.


Common Beginner Mistakes

Forgetting the Semicolon

You may write:

let name = "Rust"

instead of:

let name = "Rust";

The compiler will point out the problem.


Trying to Mutate an Immutable Variable

This won't work:

let age = 20;
age = 21;

Use:

let mut age = 20;
age = 21;

Adding a Semicolon to an Implicit Return

This:

fn add() -> i32 {
    10 + 20
}

returns 30.

But:

fn add() -> i32 {
    10 + 20;
}

does not return the integer expression.

This is a very common beginner mistake.


Practice Exercises

Before moving forward, try these exercises yourself.

Exercise 1

Create a program that prints:

My name is Rahul.
I am learning Rust.
Rust is powerful.

Exercise 2

Create two variables:

length = 10
width = 5

Calculate the area of a rectangle.


Exercise 3

Create a function:

square(number)

that returns the square of a number.

For example:

square(5) → 25

Exercise 4

Create a function that accepts a person's name and prints:

Hello, NAME!
Welcome to Rust.

Exercise 5

Build a calculator with functions for:

  • Addition
  • Subtraction
  • Multiplication
  • Division

Don't worry about user input yet.

We'll learn input handling later.


What You Learned

You now understand the fundamental structure of Rust programs.

You learned:

  • fn
  • main
  • Functions
  • Parameters
  • Return values
  • println!
  • Variables
  • let
  • mut
  • Shadowing
  • Statements
  • Expressions
  • Semicolons
  • Comments
  • String literals
  • Basic numbers
  • Arithmetic
  • Type annotations
  • Type inference
  • Code blocks
  • Naming conventions
  • Compiler errors

These concepts may look simple, but they form the foundation for everything that follows.

Pixels to Perfection Design that Impresses

Want to partner with us? let's innovate together