The V Programming Language: A Comprehensive Textbook Guide
Welcome to the ultimate learning guide for the V programming language! This textbook is structured specifically to take you from a complete beginner (zero programming experience) to an advanced V developer capable of building high-performance, concurrent, and safe systems applications. Rather than treating V as a list of syntax rules, this guide emphasizes a practical path: learn the core ideas, run the examples, and build small projects as you go.
How to read this book: Each section starts with a clear explanation of a fundamental programming concept, followed by concrete V code examples. Every example contains the exact code from the repository, formatted in clean code blocks so you can easily copy and run them yourself.
Interactive Learning: You can test any code example from this guide live in your browser using the V Playground.
Repository Structure
This book is paired with a topic-based repository layout so it is easier to explore examples by concept. The structure is intentionally arranged in a learning sequence rather than as a flat list of files:
variables_and_constants/,primitive_types/, andcontrol_flow/for the foundation of the languagefunctions/andstructs/for building reusable programs and modeling dataerror_handling/,modules/, andtesting/for reliability and organizationconcurrency/,channels/,json_and_orm/,sqlite/, andnotes_api/for real-world applicationslanguage_updates_and_stdlib/for newer language features and grouped standard library examples that are easier to browse by topic
Following this structure makes it simpler to move from small examples to larger projects, and it also gives contributors a clear place to add new lessons.
A better way to think about the repo
- Start with the introductory folders when you are learning V for the first time.
- Use the middle sections when you are ready to write more structured programs.
- Explore the application-oriented folders once you want to build something practical.
Contributing new content
When adding a new lesson, keep it in the most relevant topic folder and use a numbered naming pattern such as 01_topic_name/ so the learning flow stays predictable.
Quick Start: Learn V by Building Things
If you are new to programming, the fastest way to learn V is to start small and build something real. Follow this sequence:
1. Compile V from Source (Official Method)
Always compile V directly from source. Installing V via third-party package managers like Homebrew is not officially supported by the creators of V and frequently causes outdated builds and broken standard library resolution. Compiling from source is fast and guarantees you have the latest stable features.
# Clone the repository
git clone https://github.com/vlang/v
cd v
# Compile V compiler
make
# Create a global symlink so `v` is available system-wide
sudo ./v symlink
Verify your installation:
v version
v-analyzer Configuration: If you use VSCode or the Antigravity IDE with the v-analyzer language server, set custom_vroot in your settings to your cloned V repository path (e.g. ~/v or /path/to/v). This allows v-analyzer to properly index the vlib standard library and provide code completion and hover documentation.
2. Create Your First Program
Create a file named hello.v:
fn main() {
println('Hello, V!')
}
3. Run and Build
- Run immediately:
v run hello.v - Build a native binary:
v -o hello hello.v
V has a few ideas that are worth remembering early:
- Variables are immutable by default, so use
mutwhen you need to change a value. - Modules help organize larger programs.
optionandresultmake error handling explicit.spawnand channels make concurrency approachable.
Core Language Essentials to Learn Early
A beginner-friendly roadmap becomes much clearer when you call out the core concepts that show up again and again in V programs:
mutand immutable-by-default variables- functions, parameters, and return values
- structs and methods for modeling data
enumandmatchfor branching on discrete choices- modules and imports for organizing code
?and!for option and result typesdeferfor cleanup work before a function exitsunsafeonly when you truly need low-level access- type aliases and sum types once you are comfortable with structs and enums
These ideas form the backbone of most V programs, so it is worth learning them in a small, practical order rather than trying to memorize every feature at once.
Must-Learn-Before-Building Checklist
Before you start a larger project, make sure you can comfortably do the following:
- write a small program with a
mainfunction - declare variables and explain when to use
mut - define and call functions with clear parameters and return types
- model simple data with structs
- choose between
if,match, andforfor control flow - split code into modules and import them correctly
- handle optional or failing values with
?and! - use
deferfor cleanup work when needed - avoid
unsafeunless you truly need it
If you can do these reliably, you are ready to move from tiny examples to real programs.
Why This Matters
The goal is not just to memorize syntax. Each concept in this guide solves a real programming problem:
- Variables and mutability help your program store and update information safely.
- Functions let you break a program into small, reusable pieces.
- Structs help you model real-world data such as users, files, or payments.
- Error handling makes programs more predictable and easier to debug.
- Concurrency helps programs do more work efficiently when tasks can run independently.
When you learn a new feature, ask yourself: “What problem does this solve?” and “How would I use it in a small program?”
Suggested Learning Path
A beginner-friendly path through this guide is:
- Start with Chapters 1-4 to learn the basic syntax and control flow.
- Move into functions, structs, and modules to structure your programs.
- Practice with tests and error handling before tackling larger projects.
- Finish with concurrency, JSON, and databases by building a small app.
Next-Level Language Features to Explore
Once the basics feel natural, the next step is to expand your comfort with a few higher-level features:
- type aliases for clearer naming
- sum types for values that can be one of several shapes
- generics for reusable data structures and helpers
- interfaces for shared behavior across different types
- higher-order functions and function values
unsafeand pointers only when you need low-level performance or interop
These features are not required for every beginner project, but they become very useful once you start writing more structured or reusable code.
Mini Projects to Try
These projects will make the guide feel much more practical:
- A command-line to-do list
- A number guessing game
- A simple file organizer or text search tool
- A notes app that stores data in JSON or SQLite
- A macOS desktop app with a native Cocoa UI: vlang_simplegui
- A macOS desktop app with a webview-based UI: vlang_macos_webview_app_template
Practice Exercises
Try these small exercises as you move through the guide:
- Write a program that prints your name and age.
- Create a function that adds two numbers and returns the result.
- Build a tiny program that stores a user in a struct and prints the fields.
- Write a loop that prints the first 10 even numbers.
- Use an
optionorresultin a small helper function and handle the failure case.
If you get stuck, write the smallest possible version first and test it before adding more features.
Your First Project: A Tiny CLI Greeting App
A great first project is a small command-line app that asks for a name and prints a greeting. This lets you practice variables, functions, input, and output without getting overwhelmed.
Step 1: Start with a simple main function
fn main() {
println('Hello, V!')
}
Step 2: Add a name variable
fn main() {
name := 'Ada'
println('Hello, ' + name + '!')
}
Step 3: Make it interactive
import os
fn main() {
name := os.input('What is your name? ')
println('Hello, ' + name + '!')
}
Step 4: Improve it with a function
import os
fn greet(name string) string {
return 'Hello, ' + name + '!'
}
fn main() {
name := os.input('What is your name? ')
println(greet(name))
}
Why this project is useful
This project teaches the core flow of programming in V:
- write a small program
- test it
- add a feature
- refactor it into functions
- make the output clearer
Once this feels easy, you can extend it with a command-line option, a loop, or a saved history file.
What Most Programmers Want Next
A strong guide should help readers move from learning syntax to building and debugging real software. The following topics are especially useful for most programmers:
Quick Reference
- Run a file:
v run hello.v - Build an executable:
v -o hello hello.v - Use
mutwhen a value needs to change - Use functions to keep logic organized
- Use modules to split larger projects into manageable files
- Use tests to verify behavior as you grow your program
Common Beginner Mistakes
- Forgetting to make a variable
mutbefore changing it - Mixing up declaration and assignment
- Writing code without small, testable functions
- Trying to learn too many concepts at once instead of building one small feature
Debugging and Reading Errors
When something fails, focus on the first compiler error, reduce the problem to a smaller example, and test one change at a time. This is often faster than changing many lines at once.
Real-World Workflow
As your projects grow, you will want to know how to:
- split code into modules
- write tests
- structure folders clearly
- read documentation and standard library examples
- move from small scripts to larger applications
From Practice to Real Projects
Once the basics feel comfortable, the next step is to build small applications that combine multiple ideas. A very effective progression is:
- Build a tiny CLI tool that reads input and prints output.
- Add functions, structs, and tests.
- Introduce modules so the code is easier to maintain.
- Add file I/O or JSON handling for persistence.
- Explore concurrency for tasks that can run in parallel.
This progression helps learners move from “I can read examples” to “I can build useful software.”
Where to Go Next
After finishing this guide, the best next steps are:
- read the official V documentation and examples
- try the V Playground for rapid experimentation
- build one project end to end instead of reading only
- contribute to or study real V repositories for idiomatic patterns
Beginner Project Roadmap
A practical roadmap for new V developers could look like this:
Milestone 1: CLI App
Build a small command-line app that accepts user input and prints useful output. This helps you practice functions, variables, and control flow.
Milestone 2: Data App
Add file I/O or JSON handling so your app can save and load data. This introduces practical patterns for real-world applications.
Milestone 3: Structured App
Split the app into modules and add structs for data models. This is where programs become easier to maintain.
Milestone 4: Tested App
Write tests and improve reliability. This is an important step for building confidence as a programmer.
Milestone 5: Concurrent App
Explore concurrency with spawn and channels for tasks that can run in parallel. This is where V becomes especially compelling for performance-oriented software.
Setup Checklist
Before you start coding in V, make sure you have:
- V installed and available on your terminal
- a text editor or IDE with basic syntax highlighting
- a way to run and test small programs quickly
- a folder for practice files and mini projects
Quick Glossary
mut: makes a variable changeablefn: defines a functionstruct: defines a custom data typemodule: groups related code togetheroption/result: explicit ways to handle missing or failing valuesspawn: runs code concurrentlychannel: passes data between concurrent tasks
How to Use This Guide Effectively
To get the most from this book:
- Read the explanation first, but do not stop there.
- Run each example locally.
- Change one small thing and observe what happens.
- Write your own tiny version before moving on.
- Apply each idea in a small project as soon as possible.
- Chapter 1: Getting Started with V
- Under the Hood: The V Compilation Pipeline & Architecture
- Code Comments
- Chapter 2: Variables and Constants
- Under the Hood: Immutability, Memory Layout & Constant Inlining
- Constants
- Variables
- Chapter 3: Primitive Data Types
- Under the Hood: Primitive Data Representation, Strings & UTF-8
- Primitive Types Demo
- Boolean Type
- Numeric Types
- Rune Type
- String Type
- Chapter 4: Control Flow
- Under the Hood: Control Flow Optimization, Jump Tables & Defer Stack
- Control Flow Extras
- Chapter 5: Collections: Arrays and Maps
- Under the Hood: Dynamic Arrays, Slicing & Hash Table Mechanics
- Arrays
- Maps
- Chapter 6: Functions
- Under the Hood: Calling Conventions, Multiple Returns & Closures
- Advanced Function Features
- Function Extras
- Chapter 7: Structs (Custom Types)
- Under the Hood: Struct Alignment, Method Dispatch & Heap Allocation
- Struct Basics & Fields
- Chapter 8: Error Handling
- Under the Hood: Option (?T) and Result (!T) Representation
- Option & Result Types
- Chapter 9: Organizing Code with Modules
- Under the Hood: Module Resolution, Namespaces & VPM
- Modules & Project Structure
- Installing External Packages
- Chapter 10: Writing Tests in V
- Under the Hood: Test Discovery & The Assertion Engine
- Assertions & Unit Testing
- Chapter 11: Concurrency and Channels
- Under the Hood: V-Routines, Thread Pooling & Channel Ring Buffers
- Channels & Communication
- V-Routines & Concurrency
- Chapter 12: Working with Databases and JSON
- Under the Hood: Compile-Time Reflection & Type-Safe ORM
- Case Study: Notes API
- JSON & ORM
- SQLite Integration
- SQLite CRUD Helper
- Sqlite Raw Crud
- Chapter 13: Standard Library & Advanced Features
- Under the Hood: Sum Types, Generics, Memory Models & C Interop
- Inline Assembly & C Interop
- Networking (TCP, UDP, SSL, WebSockets)
- Other Stdlib Updates
- Strings.Lorem Helper
- WebAssembly Compilation
- Chapter 14: Useful Boilerplates and Application Templates
- CLI Command-Line Application Boilerplate
- REST API Server Boilerplate
- Worker Pool Concurrency Boilerplate
- OS and File Utilities Boilerplate
- String Utilities Boilerplate
- Math and Statistics Boilerplate
- Array Utilities Boilerplate
- Chapter 15: Comprehensive Practice Exercises
- Practice Exercises Overview
Chapter 1 Getting Started with V
Quick Access
Below is an index of all code examples in this chapter. You can use these links to jump directly to any specific code example:
Code Comments
This chapter introduces the core design philosophies of V. You will learn how to set up your development environment, compile and run programs, and document your code using comments.
Under the Hood: The V Compilation Pipeline & Architecture
Understanding how V compiles and executes your code is fundamental to writing high-performance, idiomatic applications. Unlike languages that rely on virtual machines or heavy runtime interpreters, V is a native compiled language with a uniquely fast and transparent pipeline.
The 5 Stages of the V Compiler Pipeline
- Single-Pass Parsing & AST Generation:
The V compiler reads source files in a single pass, constructing a streamlined Abstract Syntax Tree (AST). V deliberately omits complex macro expansion phases or runtime template interpretation, allowing AST construction to finish in milliseconds.
- Static Type Checking & Safety Analysis:
The compiler enforces V's core safety invariants: immutability by default, absence of unhandled null values, no variable shadowing, and bounds checking on array accesses. Type checking and compile-time reflection are resolved entirely during this pass.
- C99 Source Code Emission:
Rather than inventing an unoptimized intermediate machine-code generator, V transforms the AST directly into clean, human-readable C99 source code. This design grants V immediate compatibility with existing C libraries, debuggers (GDB, LLDB), and CPU architectures.
- Native C Compiler Execution:
V invokes a backend C compiler to produce the final machine code:
- TCC (Tiny C Compiler): Used by default in development mode. TCC compiles C code in under 0.1 seconds, delivering near-instant
v runfeedback loops. - Clang / GCC: Used when building release binaries (
v -prod). Clang/GCC apply advanced optimization passes, Link-Time Optimization (LTO), and vectorization.
- Direct Machine Code Generation (
v -native):
In addition to C emission, V contains built-in native backends for x86_64, ARM64, and WebAssembly (wasm32). These backends emit raw machine code directly without invoking an external C compiler.
+----------------+ +-------------+ +-------------------+ +--------------------+ +---------------+
| .v Source | ---> | Single-Pass | ---> | Static Safety & | ---> | C99 Emission or | ---> | Native Binary |
| Files | | Parser/AST | | Type Verification | | Direct x64/ARM/Wasm| | (< 1MB size) |
+----------------+ +-------------+ +-------------------+ +--------------------+ +---------------+
Essential Compiler Flags Reference
| Flag | Purpose | Description |
| :--- | :--- | :--- |
| v run <file.v> | Rapid Development | Compiles in memory with TCC and executes immediately. |
| v -prod <file.v> | Production Release | Emits optimized C, applies -O3 / -flto, and strips debug symbols for minimal binary size. |
| v -g <file.v> | Debugging | Generates DWARF debug symbols for use with GDB / LLDB / VSCode debugger. |
| v -show-c-output <file.v> | Inspection | Prints or saves the generated C code so you can inspect compiler output directly. |
| v fmt -w <file.v> | Formatting | Formats the source code according to the official V style guide. |
| v -os <target> | Cross-Compilation | Cross-compiles for windows, linux, macos, or wasm32-wasi. |
Compiler Inspection Tip: You can always inspect the exact C code V generates for any program by passing v -o output.c hello.v. This is an incredible learning tool for understanding how V's high-level constructs translate to efficient, low-level C instructions.
Code Comments
Single Line Comments
Single Line Comments
Comments are non-executable lines of text in a program that explain what the code does. They are ignored by the compiler but are essential for human developers. This lesson on Single Line Comments demonstrates how to write and format comments in V.
module main
// greet function prints greetings to the console
pub fn greet() {
println('Hello, Welcome to the Jungle!')
}
fn main() {
greet()
}
Multi Line Comments
Multi Line Comments
In V, multi-line (or block) comments are enclosed between /* and */.
Nested Block Comments: Unlike languages like C, C++, Java, or JavaScript, V supports nested block comments. This is a powerful feature that allows you to easily comment out large blocks of code even if they already contain block comments, without triggering syntax errors.
Code Hints & v-analyzer: Block comments (/* ... */) do not appear in code hints or hover tooltips when using v-analyzer (or v doc). To ensure your comments show up in IDE code hints and documentation, you must use single-line comments (//) on each line directly above the function, struct, or declaration.
module main
/*
multiply is a function that accepts two integer arguments (x and y).
It performs multiplication and returns the integer product.
Note: Comments formatted like this (/* ... */) do not show in code hints
using v-analyzer or `v doc`. You must use `//` for each line to enable IDE hints.
/*
Note: In V, block comments can be nested.
This is a nested block comment. In standard C, nesting block comments
would cause a compile error, but V's compiler parses them correctly.
*/
This is the end of the outer block comment.
*/
fn multiply(x int, y int) int {
return x * y
}
fn main() {
println(multiply(4, 5))
}
Programm Commented All Places
Programm Commented All Places
Comments are non-executable lines of text in a program that explain what the code does. They are ignored by the compiler but are essential for human developers. This lesson on Programm Commented All Places demonstrates how to write and format comments in V.
module main
// Space3D A struct indicating the 3 dimensional coordinate system
struct Space3D {
mut:
x int
// x is an integer field that represents coordinate
y int
// y is an integer field that represents coordinate
z int
// z is an integer field that represents coordinate
}
/*
get_point is a function that returns a struct of Type Space3D with points x,y,z passed as input arguments to it
x is an input argument accepts values of type of int
y is an input argument accepts values of type of int
z is an input argument accepts values of type of int
get_point function returns a Struct result of type Space3D with its coordinates set as value passed as input arguments x, y and z
*/
fn get_point(x int, y int, z int) Space3D {
return Space3D{
x: x
y: y
z: z
}
}
const origin = get_point(0, 0, 0)
// Defining origin as a constant
fn main() {
// origin := Space3D {x: 0, y: 0, z:0}
println(origin)
}
Chapter 2 Variables and Constants
Quick Access
Below is an index of all code examples in this chapter. You can use these links to jump directly to any specific code example:
Constants
- Define Single Constant
- Define Multiple Constants
- Define Constant Of Type Struct
- Define Constant Of Type Function
- Define Module Level Constants
- Cannot Define Constants Inside Functions
- Constants Module - Main (main.v)
- Constant Module Prefix - Helper (file1.v)
Variables
- Parallel Declaration Immutable Variables
- Parallel Declaration Mutable Variables
- Parallel Declaration Mut And Immutable Vars
- Augmented Assignment String
- Augmented Assignment Integer
- Declare Mutable Variable
- Cannot Update Mutable With Another Type
- Declare Immutable Variable
- Cannot Update Immutable Variables
- Declared And Assigned
- Declared And Not Assigned
- Unused Variables Will Be Warned
- Global Variables Not Allowed - Scope Demo
- Global Variables Not Allowed - File Scope Demo
- Variable Redeclaration
- Variable Scope For Same Variable Names
- Variable Shadowing Not Allowed
Variables are the basic storage units of any program. In this chapter, we explore how V handles variables with a safety-first mindset: variables are immutable by default, variable shadowing is forbidden, and constants are declared in module scopes. You will learn to manage program data safely and cleanly.
Under the Hood: Immutability, Memory Layout & Constant Inlining
V approaches variables and constants with a strict safety-first design philosophy. Understanding how memory is laid out and how the compiler optimizes bindings eliminates common debugging traps.
Memory Representation of Immutable vs Mutable Bindings
* Immutable Variables (x := 10):
When a variable is declared without mut, the V compiler registers it as an immutable binding. In the emitted C code, this translates to a const local variable allocated on the CPU stack. The compiler statically rejects any assignment or modification to this memory address.
* Mutable Variables (mut y := 10):
Declaring a variable with mut creates a standard mutable stack slot. Modifying y = 20 alters the value stored at that stack offset in-place without heap allocation.
* Type Rigidity:
Once a variable is declared, its type is permanently fixed. V prohibits changing the type of a mutable variable (e.g., assigning a string to an integer variable), preventing dynamic type confusion bugs.
Stack Frame:
+-------------------------------+-----------------------------------+
| const int x = 10 (Immutable) | int y = 20 (Mutable Stack Slot) |
+-------------------------------+-----------------------------------+
Zero-Cost Compile-Time Constants
Constants declared in const (...) blocks possess unique runtime characteristics:
- Compile-Time Folding: Scalar constants (integers, floats, string literals) are evaluated and folded during compilation. The compiler substitutes the literal value directly at the callsite.
- Global Read-Only Data (
.rodata): Complex constants (structs, arrays) are emitted as static read-only symbols placed in the.rodatabinary section, incurring zero initialization runtime cost when your application boots. - Module Scoping: Constants are module-level and cannot be declared inside function bodies. This enforces clean separation between constant domain configuration and local procedural state.
Why Variable Shadowing is Strictly Prohibited
In many languages (such as JavaScript, Python, C++, and Rust), an inner block scope can redeclare a variable with the same name as an outer scope:
// FORBIDDEN IN V:
fn example() {
count := 10
if true {
count := 20 // Compile Error: duplicate variable name `count`
}
}
V treats variable shadowing as a compile-time error. Shadowing is one of the most frequent sources of subtle logic bugs in large codebases (where an engineer intends to update an outer variable but accidentally declares a new local variable). By disallowing shadowing, V guarantees that every identifier within a function refers unambiguously to a single declared memory location.
Constants
Define Single Constant
Define Single Constant
Constants in V are defined using the const block. Constants are values that are known at compile time and never change throughout the execution of the program. By convention, constant names are written in lowercase, unlike many other languages.
This example shows how to define and use a single constant.
const app_name = 'V on Wheels'
fn main() {
println(app_name)
}
Define Multiple Constants
Define Multiple Constants
You can define multiple constants within a single const block. This keeps related constants grouped together and makes the code cleaner.
This example shows how to declare multiple constants (integers, strings, floats) together.
const app_name = 'V on Wheels'
const max_connections = 1000
const decimal_places = 2
const pi = 3.14
fn main() {
println(app_name)
println(max_connections)
println(decimal_places)
println(pi)
}
Define Constant Of Type Struct
Define Constant Of Type Struct
Variables and constants store state in V programs. This lesson on Define Constant Of Type Struct covers declaration rules, default values, scopes, or constant naming conventions.
module main
struct Space3D {
mut:
x int
y int
z int
}
const origin = Space3D{
x: 0
y: 0
z: 0
}
fn main() {
println(origin)
}
Define Constant Of Type Function
Define Constant Of Type Function
Variables and constants store state in V programs. This lesson on Define Constant Of Type Function covers declaration rules, default values, scopes, or constant naming conventions.
module main
struct Space3D {
mut:
x int
y int
z int
}
fn get_point(x int, y int, z int) Space3D {
return Space3D{
x: x
y: y
z: z
}
}
const origin = get_point(0, 0, 0)
fn main() {
println(origin)
}
Define Module Level Constants
Define Module Level Constants
Variables and constants store state in V programs. This lesson on Define Module Level Constants covers declaration rules, default values, scopes, or constant naming conventions.
module main
const app_name = 'V on Wheels'
fn main() {
println(app_name)
}
Cannot Define Constants Inside Functions
Cannot Define Constants Inside Functions
Variables and constants store state in V programs. This lesson on Cannot Define Constants Inside Functions covers declaration rules, default values, scopes, or constant naming conventions.
module main
const app_name = 'V on Wheels'
fn main() {
const greet = 'hi' // this is not top level constant definition, throws error.
println(app_name)
}
Constants Module - Main (main.v)
Constants Module - Main
Variables and constants store state in V programs. This lesson on Main covers declaration rules, default values, scopes, or constant naming conventions.
module main
import mod1
fn main() {
mod1.do_work()
}
Constant Module Prefix - Helper (file1.v)
Constant Module Prefix - Helper
Variables and constants store state in V programs. This lesson on File1 covers declaration rules, default values, scopes, or constant naming conventions.
module mod1
const greet_count = 5
pub fn do_work() {
println(greet_count)
}
Variables
Parallel Declaration Immutable Variables
Parallel Declaration Immutable Variables
In V, you can declare and initialize multiple variables in a single line. This is known as parallel declaration. By default, variables in V are immutable (read-only). Once assigned a value, they cannot be changed.
This program demonstrates declaring two variables a and b at the same time and assigning them initial values. Any attempt to modify a or b later in the code will cause a compile-time error.
fn main() {
first_name, last_name, age := 'Ada', 'Lovelace', 36
println('${first_name} ${last_name} is ${age} years old')
println('Next milestone: ${first_name} will speak at the conference')
}
Parallel Declaration Mutable Variables
Parallel Declaration Mutable Variables
If you want to modify parallelly declared variables later, you must explicitly mark them as mutable using the mut keyword. In V, mutability is always explicit to make code safer and easier to reason about.
Here, we declare two mutable variables a and b at the same time using mut. We then reassign their values using the standard assignment operator (=).
fn main() {
mut greeting, mut recipient := 'Hi', 'world'
println('${greeting}, ${recipient}!')
greeting, recipient = 'Hello', 'Ada'
println('${greeting}, ${recipient}!')
}
Parallel Declaration Mut And Immutable Vars
Parallel Declaration Mut And Immutable Vars
Variables and constants store state in V programs. This lesson on Parallel Declaration Mut And Immutable Vars covers declaration rules, default values, scopes, or constant naming conventions.
fn main() {
mut message, count := 'Hello', 32
println(message)
message = 'Hi'
println(message)
println(count)
}
Augmented Assignment String
Augmented Assignment String
Variables and constants store state in V programs. This lesson on Augmented Assignment String covers declaration rules, default values, scopes, or constant naming conventions.
fn main() {
mut greeting := 'Hi'
println(greeting)
greeting = greeting + ' there'
println(greeting)
greeting += ', how are you today?'
println(greeting)
}
Augmented Assignment Integer
Augmented Assignment Integer
Variables and constants store state in V programs. This lesson on Augmented Assignment Integer covers declaration rules, default values, scopes, or constant naming conventions.
fn main() {
mut score := 10
println(score)
score = score + 5
println(score)
score += 5
println(score)
}
Declare Mutable Variable
Declare Mutable Variable
By default, all variables in V are immutable (their values cannot change). To declare a variable whose value can be modified later, you must prepend the mut keyword before the variable name.
This example shows how to declare a mutable variable, change its value, and print the results.
fn main() {
mut counter := 0
counter += 1
println(counter)
}
Cannot Update Mutable With Another Type
Cannot Update Mutable With Another Type
Variables and constants store state in V programs. This lesson on Cannot Update Mutable With Another Type covers declaration rules, default values, scopes, or constant naming conventions.
fn main() {
mut i := 10
i = 100
i = 'Apple' // throws error
}
Declare Immutable Variable
Declare Immutable Variable
In V, variables are immutable by default. This design choice prevents accidental state mutation bugs, making code easier to reason about and safer for concurrency. When you declare a variable using the declaration operator :=, you are creating a read-only variable. If you try to reassign this variable later, the compilation will fail. This approach is similar to declaring constants in other languages, but it operates at the local scope level.
This example demonstrates how to declare an immutable variable and print its value.
fn main() {
// 'msg' is initialized as an immutable string variable using :=
msg := 'Hello'
println(msg)
}
Cannot Update Immutable Variables
Cannot Update Immutable Variables
One of V's core safety features is immutability by default. If you declare a variable without the mut keyword and then try to reassign it a new value, the compiler will refuse to compile the program.
This example demonstrates what happens when you try to update an immutable variable (expect a compiler error).
fn main() {
msg := 'Hello'
msg = 'Good Day!' // throws error
}
Declared And Assigned
Declared And Assigned
Variables and constants store state in V programs. This lesson on Declared And Assigned covers declaration rules, default values, scopes, or constant naming conventions.
fn main() {
mut i := 0
// declared and assigned
println(i)
}
Declared And Not Assigned
Declared And Not Assigned
V does not allow variables to be declared without an initial value. Unlike other languages that initialize variables to a default 'zero' value or null, V forces you to explicitly provide a value. This prevents uninitialized variable bugs.
This example illustrates that declaring a variable without an assignment is a compilation error.
fn main() {
mut a // throws error
}
Unused Variables Will Be Warned
Unused Variables Will Be Warned
To keep codebases clean and efficient, the V compiler detects if you declare a variable but never use (consume) it. By default, V treats unused variables as a compilation warning/error, encouraging you to clean up dead code.
This example shows a declared variable that is never used.
fn main() {
i := 'hello' // i is not used anywhere, so warns when run in dev mode and throws error when run in prod mode
x := 3
y := 2
println(x + y)
}
Global Variables Not Allowed - Scope Demo
Global Variables Not Allowed - Scope Demo
V does not allow global variables by default. Global state is a major source of bugs, race conditions in multi-threaded applications, and poor code structure. By forbidding globals, V enforces clean, modular code passing state via arguments.
These examples demonstrate that declaring variables outside of the main function or modules is strictly prohibited.
module main
fn method1() {
msg := 'Hello from Method1'
println(msg)
}
fn main() {
method1()
println(msg) // Will throw error as msg declared and accessible only in method1
}
Global Variables Not Allowed - File Scope Demo
Global Variables Not Allowed - File Scope Demo
V does not allow global variables by default. Global state is a major source of bugs, race conditions in multi-threaded applications, and poor code structure. By forbidding globals, V enforces clean, modular code passing state via arguments.
These examples demonstrate that declaring variables outside of the main function or modules is strictly prohibited.
module main
fn method1() {
if true {
mut b := 10
b++
}
println(b)
}
fn main() {
method1()
}
Variable Redeclaration
Variable Redeclaration
Variables and constants store state in V programs. This lesson on Variable Redeclaration covers declaration rules, default values, scopes, or constant naming conventions.
module main
fn main() {
x := 3
y := 2
println(x + y)
x := 5 // re-definition of variable x is not allowed
}
Variable Scope For Same Variable Names
Variable Scope For Same Variable Names
In V, variables are strictly scoped to the function or block in which they are declared. This lexical scoping means that two different functions can declare variables with the exact same name (e.g., msg) without any collision or interference. The compiler guarantees that these variables occupy separate locations in memory and are completely isolated from one another. This allows developers to use common, context-appropriate names like temp, id, or msg locally inside individual functions without worrying about global or cross-functional namespace pollution.
This program illustrates how msg is declared separately in both method1 and method2, showing scope isolation in action.
module main
fn method1() {
// 'msg' is local only to method1
msg := 'Hello from Method1'
println(msg)
}
fn method2() {
// 'msg' is local only to method2; does not conflict with method1's 'msg'
msg := 'Hello from Method2'
println(msg)
}
fn main() {
method1()
method2()
}
Variable Shadowing Not Allowed
Variable Shadowing Not Allowed
Variable shadowing happens when a variable declared within an inner scope (like an if block, a loop, or a function body) has the same name as a variable in an outer scope. V strictly forbids variable shadowing at the compiler level. Prohibiting shadowing prevents a class of common bugs where a developer accidentally updates a local inner variable instead of the intended outer variable, or vice versa.
This example demonstrates how V rejects shadowed variable declarations.
module main
fn scope_demo() {
// 'x' is declared in the function's main scope
x := 10
println(x)
if true {
// ERROR: Declaring another variable named 'x' in an inner block is forbidden.
// To fix this, you must name the inner variable something else.
x := 20
println(x)
}
println(x)
}
fn main() {
scope_demo()
}
Chapter 3 Primitive Data Types
Quick Access
Below is an index of all code examples in this chapter. You can use these links to jump directly to any specific code example:
Primitive Types Demo
Boolean Type
Numeric Types
- Declaring Integers
- Hex Binary Octa Notation Of Declaring Integers
- Promoting Numeric Types
- Arithmetic Operators
- Bitwise Operators
- Shift Operators
- Shift Operator On Range Of Integers
- Integer Methods
- Float Methods
- U8 Methods
- Size Pointer Methods
Rune Type
String Type
- Declare String
- String Read Only Array Of Bytes
- Strings Immutable By Default
- Declaring Mutable Strings
- Cannot Mutate String Elements
- String Interpolation
- Escape Special Characters
- Declare Raw Strings
- String Concatenation Using Plus Sign
- String Concatenation Using Interpolation
- Extract Substring From String Literal
- Split String
- String To Runes Array
- Count Sub String Occurences
- Check String Contains Substring
- String Contains Is Case Sensitive
- Common String Methods
Primitive Types Demo
Primitive Types Demo Code
Primitive Types Demo Code
This comprehensive example demonstrates every primitive data type in V:
- Boolean:
bool(representingtrueorfalse). - String:
string(representing an immutable array of bytes). - Rune:
rune(representing a single Unicode code point, alias foru32). - Signed Integers:
i8(8-bit),i16(16-bit),int(32-bit),i64(64-bit). - Unsigned Integers:
u8(8-bit),u16(16-bit),u32(32-bit),u64(64-bit). - Platform-dependent sizes:
isize(signed size of a pointer),usize(unsigned size of a pointer). - Floating Point Numbers:
f32(32-bit single-precision),f64(64-bit double-precision).
For each type, the example initializes a value and prints its value, type (using typeof(var).name), and size in bytes (using sizeof(var)).
module main
fn main() {
println('==================================================')
println(' Vlang Primitive Data Types Demo ')
println('==================================================')
// 1. Boolean Type
b := true
println('Boolean: val: ${b} | type: ${typeof(b).name} | size: ${sizeof(b)} byte')
// 2. String Type
s := 'Hello, V!'
println('String: val: "${s}" | type: ${typeof(s).name} | size: ${sizeof(s)} bytes')
// 3. Rune Type (unicode character, represented as `r` prefix or backticks)
r := `V`
println('Rune: val: ${r} (char: ${r.str()}) | type: ${typeof(r).name} | size: ${sizeof(r)} bytes')
// 4. Signed Integers
i_8 := i8(-128)
i_16 := i16(-32768)
i_32 := int(-2147483648)
i_64 := i64(-9223372036854775808)
println('i8: val: ${i_8} | type: ${typeof(i_8).name} | size: ${sizeof(i_8)} byte')
println('i16: val: ${i_16} | type: ${typeof(i_16).name} | size: ${sizeof(i_16)} bytes')
println('int: val: ${i_32} | type: ${typeof(i_32).name} | size: ${sizeof(i_32)} bytes')
println('i64: val: ${i_64} | type: ${typeof(i_64).name} | size: ${sizeof(i_64)} bytes')
// 5. Unsigned Integers
u_8 := u8(255)
u_16 := u16(65535)
u_32 := u32(4294967295)
u_64 := u64(18446744073709551615)
println('u8: val: ${u_8} | type: ${typeof(u_8).name} | size: ${sizeof(u_8)} byte')
println('u16: val: ${u_16} | type: ${typeof(u_16).name} | size: ${sizeof(u_16)} bytes')
println('u32: val: ${u_32} | type: ${typeof(u_32).name} | size: ${sizeof(u_32)} bytes')
println('u64: val: ${u_64} | type: ${typeof(u_64).name} | size: ${sizeof(u_64)} bytes')
// 6. Platform-dependent Sizes
isize_val := isize(-12345)
usize_val := usize(12345)
println('isize: val: ${isize_val} | type: ${typeof(isize_val).name} | size: ${sizeof(isize_val)} bytes')
println('usize: val: ${usize_val} | type: ${typeof(usize_val).name} | size: ${sizeof(usize_val)} bytes')
// 7. Floating Point Numbers
f_32 := f32(3.14159)
f_64 := f64(2.718281828459)
println('f32: val: ${f_32} | type: ${typeof(f_32).name} | size: ${sizeof(f_32)} bytes')
println('f64: val: ${f_64} | type: ${typeof(f_64).name} | size: ${sizeof(f_64)} bytes')
println('==================================================')
}
V is a statically-typed language, meaning every variable has a fixed data type at compile time. In this chapter, you will learn about V's primitive types: booleans for logic, numeric types for numbers, runes for single characters, and strings for text. You will also learn about V's rich set of built-in methods on these types.
Under the Hood: Primitive Data Representation, Strings & UTF-8
Every primitive type in V is engineered for predictable memory footprint, binary compatibility with C, and strict safety.
Primitive Memory Map & Sizing
| V Type | C Equivalent | Size (Bytes) | Range / Purpose |
| :--- | :--- | :--- | :--- |
| bool | bool | 1 byte | true or false |
| i8 / u8 | int8_t / uint8_t | 1 byte | -128 to 127 / 0 to 255 (byte) |
| i16 / u16 | int16_t / uint16_t | 2 bytes | 16-bit signed / unsigned integers |
| int / i32 / u32 | int32_t / uint32_t | 4 bytes | Standard 32-bit integer |
| i64 / u64 | int64_t / uint64_t | 8 bytes | 64-bit signed / unsigned integers |
| f32 / f64 | float / double | 4 / 8 bytes | IEEE 754 floating-point numbers |
| rune | uint32_t | 4 bytes | Unicode code point (u32) |
| isize / usize | ptrdiff_t / size_t | 4 or 8 bytes | Pointer-sized integers |
The Memory Layout of a V `string`
In V, a string is a 16-byte structure on 64-bit systems. It is defined internally as:
struct string {
pub:
str &u8 // Pointer to raw UTF-8 byte array (null-terminated)
len int // Length of the string in bytes
is_lit int // 1 if string literal (static .rodata), 0 if heap-allocated
}
Key architectural benefits of this design:
- Zero-Copy C Interoperability: Because the underlying
strbyte buffer is guaranteed to be null-terminated (