Search Results

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.

NOTE
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.
TIP
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:

  • variablesandconstants/, primitivetypes/, and controlflow/ for the foundation of the language
  • functions/ and structs/ for building reusable programs and modeling data
  • error_handling/, modules/, and testing/ for reliability and organization
  • concurrency/, channels/, jsonandorm/, sqlite/, and notes_api/ for real-world applications
  • languageupdatesand_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 01topicname/ 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. Install V and confirm it works with v --version.
  2. Create a file named hello.v with a tiny program:
v
Run
fn main() {
	println('Hello, V!')
}
  1. Run it with v run hello.v.
  2. Build an executable with v -o hello hello.v.

V has a few ideas that are worth remembering early:

  • Variables are immutable by default, so use mut when you need to change a value.
  • Modules help organize larger programs.
  • option and result make error handling explicit.
  • spawn and channels make concurrency approachable.

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.

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

Practice Exercises

Try these small exercises as you move through the guide:

  1. Write a program that prints your name and age.
  2. Create a function that adds two numbers and returns the result.
  3. Build a tiny program that stores a user in a struct and prints the fields.
  4. Write a loop that prints the first 10 even numbers.
  5. Use an option or result in 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

v
Run
fn main() {
	println('Hello, V!')
}

Step 2: Add a name variable

v
Run
fn main() {
	name := 'Ada'
	println('Hello, ' + name + '!')
}

Step 3: Make it interactive

v
Run
import os

fn main() {
	name := os.input('What is your name? ')
	println('Hello, ' + name + '!')
}

Step 4: Improve it with a function

v
Run
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 mut when 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 mut before 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

V Cheat Sheet

This cheat sheet gathers the most common V patterns in one place. Use it as a quick reference while you move from examples to small programs.

Run and build

bash
v run hello.v
v -o hello hello.v

Practical examples programmers reach for

Read input and greet the user

v
Run
import os

fn main() {
	name := os.input('What is your name? ')
	println('Hello, ${name}!')
}

Work with strings

v
Run
fn main() {
	msg := 'v is simple'
	println(msg.to_upper())
	println(msg.len)
}

Read a file

v
Run
import os

fn main() {
	content := os.read_file('notes.txt') or { '' }
	println(content)
}

Variables and mutability

v
Run
fn main() {
	mut count := 0
	count++
	println(count)
}
  • := declares and initializes a variable.
  • mut makes a variable changeable.
  • Variables are immutable by default.

Conditionals and loops

v
Run
fn main() {
	age := 20

	if age >= 18 {
		println('adult')
	} else {
		println('minor')
	}

	for i in 0..5 {
		println(i)
	}
}

Functions

v
Run
fn add(a int, b int) int {
	return a + b
}
  • Keep functions small and focused.
  • Use explicit parameter and return types.

Arrays and maps

v
Run
fn main() {
	nums := [1, 2, 3]
	println(nums[0])

	grades := {'alice': 90, 'bob': 85}
	println(grades['alice'])
}

Structs

v
Run
struct User {
	name string
	age  int
}

fn main() {
	user := User{
		name: 'Ada'
		age: 36
	}
	println(user.name)
}

Error handling

v
Run
fn maybe_value() ?int {
	return 42
}

fn main() {
	value := maybe_value() or { 0 }
	println(value)
}
  • ? marks a function that may fail or produce no value.
  • or {} provides a clear fallback.

Modules

v
Run
module main

import math
  • Use modules to split larger projects into smaller files.
  • Imports make standard library features available.

Concurrency basics

v
Run
fn worker() {
	println('working')
}

fn main() {
	spawn worker()
}

Quick glossary

  • mut: make a variable changeable
  • fn: define a function
  • struct: define a custom data type
  • module: group related code together
  • ?: represent optional or failing values
  • spawn: run work concurrently

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:

  1. Build a tiny CLI tool that reads input and prints output.
  2. Add functions, structs, and tests.
  3. Introduce modules so the code is easier to maintain.
  4. Add file I/O or JSON handling for persistence.
  5. 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 changeable
  • fn: defines a function
  • struct: defines a custom data type
  • module: groups related code together
  • option/result: explicit ways to handle missing or failing values
  • spawn: runs code concurrently
  • channel: passes data between concurrent tasks

How to Use This Guide Effectively

To get the most from this book:

  1. Read the explanation first, but do not stop there.
  2. Run each example locally.
  3. Change one small thing and observe what happens.
  4. Write your own tiny version before moving on.
  5. Apply each idea in a small project as soon as possible.

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.

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.

Additional Context from Repository docs:

This example demonstrates the concepts of single line comments.

v
Run
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

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 Multi Line Comments demonstrates how to write and format comments in V.

Additional Context from Repository docs:

This example demonstrates the concepts of multi line comments.

v
Run
module main

/*
multiply is a function that accepts two integer arguments
namely x and y.
It then performs multiplication of input arguments and returns the product which is again a type of integer as specified in the function signature.
x is an input argument accepts values of type of int
y is an input argument accepts values of type of int
multiply function returns the result of type int which is a multiplication of input arguments x and y
*/
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.

Additional Context from Repository docs:

This example demonstrates the concepts of programm commented all places.

v
Run
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

Variables


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.

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.

Additional Context from Repository docs:

This example demonstrates the concepts of define single constant.

v
Run
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.

Additional Context from Repository docs:

This example demonstrates the concepts of define multiple constants.

v
Run
const app_name2 = '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.

Additional Context from Repository docs:

This example demonstrates the concepts of define constant of type struct.

v
Run
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.

Additional Context from Repository docs:

This example demonstrates the concepts of define constant of type function.

v
Run
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.

Additional Context from Repository docs:

This example demonstrates the concepts of define module level constants.

v
Run
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.

Additional Context from Repository docs:

This example demonstrates the concepts of cannot define constants inside functions.

v
Run
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.

Additional Context from Repository docs:

This example demonstrates the concepts of main.

v
Run
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.

Additional Context from Repository docs:

This example demonstrates the concepts of file1.

v
Run
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.

Additional Context from Repository docs:

This example demonstrates the concepts of parallel declaration immutable variables.

v
Run
fn main() {
	name, age, city := 'Ada', 36, 'London'
	println('${name} is ${age} years old and lives in ${city}.')
}

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 (=).

Additional Context from Repository docs:

This example demonstrates the concepts of parallel declaration mutable variables.

v
Run
// mutable variables parallel assignment

fn main() {
	mut i, mut j := 'Hi', 'Hello'
	println(i)
	println(j)

	// updating mutable variables in parallel
	i, j = 'Hi there', 'Hello, Good Day!'
}

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.

Additional Context from Repository docs:

This example demonstrates the concepts of parallel declaration mut and immutable vars.

v
Run
fn main() {
	mut msg, i := 'Hello', 32
	println(msg) // Hello
	msg = 'Hi'
	println(msg) // Hi
	println(i) // 32
	i = 2 // error: `i` is immutable, declare it with `mut` to make it mutable
}

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.

Additional Context from Repository docs:

This example demonstrates the concepts of augmented assignment string.

v
Run
fn main() {
	mut greet := 'Hi'
	println(greet)

	greet = greet + ' there, How are you?'
	println(greet)

	greet += ' Hope you have a great day!'
	println(greet)
}

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.

Additional Context from Repository docs:

This example demonstrates the concepts of augmented assignment integer.

v
Run
fn main() {
	mut cnt := 10
	println(cnt)
	cnt = cnt + 5
	println(cnt)
	cnt += 5
	println(cnt)
}

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.

Additional Context from Repository docs:

This example demonstrates the concepts of declare mutable variable.

v
Run
fn main() {
	mut count := 0
	count += 1
	count += 2
	println('Current count: ${count}')
}

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.

Additional Context from Repository docs:

This example demonstrates the concepts of cannot update mutable with another type.

v
Run
fn main() {
	mut i := 10
	i = 100
	i = 'Apple' // throws error
}

Declare Immutable Variable

Declare Immutable Variable

Variables and constants store state in V programs. This lesson on Declare Immutable Variable covers declaration rules, default values, scopes, or constant naming conventions.

Additional Context from Repository docs:

This example demonstrates the concepts of declare immutable variable.

v
Run
fn main() {
	greeting := 'Hello'
	message := greeting + ', V!'
	println(message)
}

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).

Additional Context from Repository docs:

This example demonstrates the concepts of cannot update immutable variables.

v
Run
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.

Additional Context from Repository docs:

This example demonstrates the concepts of declared and assigned.

v
Run
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.

Additional Context from Repository docs:

This example demonstrates the concepts of declared and not assigned.

v
Run
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.

Additional Context from Repository docs:

This example demonstrates the concepts of unused variables will be warned.

v
Run
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.

Additional Context from Repository docs:

This example demonstrates the concepts of global variables not allowed.

v
Run
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.

Additional Context from Repository docs:

This example demonstrates the concepts of global variables not allowed.

v
Run
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.

Additional Context from Repository docs:

This example demonstrates the concepts of variable redeclaration.

v
Run
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

Variables and constants store state in V programs. This lesson on Variable Scope For Same Variable Names covers declaration rules, default values, scopes, or constant naming conventions.

Additional Context from Repository docs:

This example demonstrates the concepts of variable scope for same variable names.

v
Run
module main

fn method1() {
	msg := 'Hello from Method1'
	println(msg)
}

fn method2() {
	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 a loop or a function block) has the same name as a variable in an outer scope. V strictly forbids variable shadowing to prevent confusion and accidental bugs where a developer modifies the wrong variable.

This example shows how V rejects shadowed variable declarations.

Additional Context from Repository docs:

This example demonstrates the concepts of variable shadowing not allowed.

v
Run
module main

fn scope_demo() {
	x := 10
	println(x)
	if true {
		x := 20 // throws error as shadowing is not allowed
		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

Rune Type

String Type

Primitive Types Demo

Primitive Types Demo Code

Primitive Types Demo Code

This comprehensive example demonstrates every primitive data type in V:

  • Boolean: bool (representing true or false).
  • String: string (representing an immutable array of bytes).
  • Rune: rune (representing a single Unicode code point, alias for u32).
  • Signed Integers: i8 (8-bit), i16 (16-bit), int (32-bit), i64 (64-bit).
  • Unsigned Integers: u8 (8-bit, alias byte), 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)).

Additional Context from Repository docs:

This example demonstrates the concepts of primitive types demo.

v
Run
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.

Boolean Type

Logical Operators

Logical Operators

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Logical Operators in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of logical operators.

v
Run
module main

fn main() {
	t := true
	f := false

	// Logical And using && operator
	and_tt := t && t
	and_tf := t && f
	and_ft := f && t
	and_ff := f && f

	println('Logical And using && operator')
	println('${t} && ${t} = ${and_tt}')
	println('${t} && ${f} = ${and_tf}')
	println('${f} && ${t} = ${and_ft}')
	println('${f} && ${f} = ${and_ff}')
	println('')

	// Logical OR using || operator
	or_tt := t || t
	or_tf := t || f
	or_ft := f || t
	or_ff := f || f

	println('Logical OR using || Operator')
	println('${t} || ${t} = ${or_tt}')
	println('${t} || ${f} = ${or_tf}')
	println('${f} || ${t} = ${or_ft}')
	println('${f} || ${f} = ${or_ff}')
	println('')

	// Logical not using ! Operator
	not_t := !t
	not_f := !f

	println('Logical not using ! Operator')
	println('!${t} = ${not_t}')
	println('!${f} = ${not_f}')
}

Relational Operators

Relational Operators

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Relational Operators in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of relational operators.

v
Run
module main

struct Note {
	id        int
	detail    string
	completed bool
}

fn main() {
	mut n := Note{
		id:     1001
		detail: 'get groceries'
	}
	println(n.completed) // un-assigned bool field will be false by default

	// Comparing using Relational operator >
	if n.id > 1000 { // comparison of note id of integer type to another integer evaluates to a boolean
		println('The note id is greater than 1000')
	} else {
		println('The note id is less than 1000')
	}

	// Comparing using Relational operator ==
	if n.detail == 'get groceries' {
		println('The note details about groceries')
	}

	// Comparing using Relational operator !=
	if n.detail != 'get dairy products' {
		println('The note does not details about dairy products')
	}
}

Boolean Methods

Boolean Methods

Booleans in V are simple true or false values. V provides built-in methods on boolean types, such as str(), which converts the boolean value to its string representation ('true' or 'false').

This is useful for logging, printing, or interpolating booleans into strings.

Additional Context from Repository docs:

This example demonstrates the concepts of boolean methods.

v
Run
module main

fn main() {
	t := true
	f := false

	// str() returns string representation ('true' or 'false')
	println(t.str()) // true
	println(f.str()) // false
}

Numeric Types

Declaring Integers

Declaring Integers

V has several built-in integer types, both signed and unsigned, of various sizes (e.g., i8, i16, i32, i64 for signed integers, and u8, u16, u32, u64 for unsigned integers). If you declare an integer using :=, V defaults to the standard 32-bit integer (int).

This example demonstrates how to declare different integer types.

Additional Context from Repository docs:

This example demonstrates the concepts of declaring integers.

v
Run
module main

fn main() {
	x := 1
	println(typeof(x).name)
	// int

	i := 1_000
	j := 1000

	println(i == j) // true
}

Hex Binary Octa Notation Of Declaring Integers

Hex Binary Octa Notation Of Declaring Integers

V has several built-in integer types, both signed and unsigned, of various sizes (e.g., i8, i16, i32, i64 for signed integers, and u8, u16, u32, u64 for unsigned integers). If you declare an integer using :=, V defaults to the standard 32-bit integer (int).

This example demonstrates how to declare different integer types.

Additional Context from Repository docs:

This example demonstrates the concepts of hex binary octa notation of declaring integers.

v
Run
module main

fn demo() {
	h1 := 0x64 // hexadecimal starts with 0x
	b1 := 0b1100100 // binary starts with 0b
	o1 := 0o144 // Octal starts with 0o
	println('Value of var h1 with hexadecimal value : ${h1}')
	println('Data type of var h1 with hexadecimal value : ${typeof(h1).name}')
	println('Value of var b1 with binary value : ${b1}')
	println('Data type of var b1 with binary value : ${typeof(b1).name}')
	println('Value of var o1 with octal value : ${o1}')
	println('Data type of var o1 with octal value : ${typeof(o1).name}')
}

fn main() {
	demo()
}

Promoting Numeric Types

Promoting Numeric Types

V is very strict about types. It does not perform implicit type conversion (coercion) between different numeric types to prevent accidental precision loss or overflow bugs. If you want to perform arithmetic operations on different types, you must explicitly cast them.

This example shows how to cast (promote) smaller integer types to larger ones or to floats.

Additional Context from Repository docs:

This example demonstrates the concepts of promoting numeric types.

v
Run
module main

fn demo() {
	ia := i8(2)
	ib := i16(2)
	ic := int(2)

	println('----type definitions----')
	println('variable ia is of type: ${typeof(ia).name}')
	println('variable ib is of type: ${typeof(ib).name}')
	println('variable ic is of type: ${typeof(ic).name}')
	println('')
	iaa := ia + ia // i8 with i8 results i8
	ibb := ib + ib // i16 with i16 results i16
	icc := ic + ic // int with int results int
	println('----mixing types----')
	println('variable iaa is of type: ${typeof(iaa).name}, after adding type ${typeof(ia).name} with itself')
	println('variable ibb is of type: ${typeof(ibb).name}, after adding type ${typeof(ib).name} with itself')
	println('variable icc is of type: ${typeof(icc).name}, after adding type ${typeof(ic).name} with itself')
	println('')
	iab := ia + ib // i8 with i16 results in i16
	ibc := ib - ic // i16 with i32 results in i32
	println('----type promotion----')
	println('variable iab is promoted to type: ${typeof(iab).name}, after adding type ${typeof(ia).name} with ${typeof(ib).name}')
	println('variable ibc is promoted to type: ${typeof(ibc).name}, after subtracting type ${typeof(ib).name} with ${typeof(ic).name}')

	iba := ib / ia // the division of i16 and i8 types
	println('Variable iba is promoted to the higher data type ${typeof(iba).name} which is carried from ib of type ${typeof(ib).name} divided from variable ia of type ${typeof(ia).name}')

	fa := f32(2)

	fa_iba := fa + iba // fa is type of f32 and iba is of type i32
	println('Variable fa_iba is promoted to the higher data type ${typeof(fa_iba).name} which is carried from fa of type ${typeof(fa).name} when added with variable iba of type ${typeof(iba).name}')
}

fn main() {
	demo()
}

Arithmetic Operators

Arithmetic Operators

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Arithmetic Operators in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of arithmetic operators.

v
Run
module main

fn main() {
	price := 120
	tax_rate := 8
	tax := price * tax_rate / 100
	total := price + tax

	println('Subtotal: ${price}')
	println('Tax: ${tax}')
	println('Total: ${total}')
}

Bitwise Operators

Bitwise Operators

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Bitwise Operators in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of bitwise operators.

v
Run
module main

fn main() {
	a := 0b00000110 // 6
	b := 0b00000010 // 2

	// bitwise AND operation of two binary nums using & operator
	b_and := a & b

	// bitwise OR operation of two binary nums using | operator
	b_or := a | b

	// bitwise XOR operation of two binary nums using ^ operator
	b_xor := a ^ b

	// bitwise NOT operation of an binary nums using ~ operator
	not_a := ~a // Not operation yields value which is equal to -(a+1) in its integer form
	println('Bitwise AND: ${a:08b} & ${b:08b} = ${b_and:08b}')
	println('Bitwise OR: ${a:08b} | ${b:08b} = ${b_or:08b}')
	println('Bitwise XOR: ${a:08b} ^ ${b:08b} = ${b_xor:08b}')
	println('Bitwise NOT: ~${a:b} = ${not_a:b}')
}

Shift Operators

Shift Operators

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Shift Operators in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of shift operators.

v
Run
module main

fn main() {
	// declare 8 bit integer with value 3
	a := i8(3)

	// 8 bits equals to 1 byte
	println('a is ${sizeof(a)} byte(s)') // a is 1 byte(s)

	// declare 8-bit unsigned integer to shift by 1 position
	pos := byte(1)

	// Shift left the value 3 by 1 position
	a_left_shift := a << pos
	println('${a} << ${pos} = ${a_left_shift}')
}

Shift Operator On Range Of Integers

Shift Operator On Range Of Integers

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Shift Operator On Range Of Integers in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of shift operator on range of integers.

v
Run
module main

fn main() {
	val := i8(1)

	bits := sizeof(val) * 8

	println('Performing left shift using << Operator')

	for i in 0 .. bits {
		after_shift := val << i
		println('$val << $i = $after_shift \/\/ type after shift operation: ${typeof(after_shift).name}')
	}
}

Integer Methods

Integer Methods

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Integer Methods in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of integer methods.

v
Run
module main

fn main() {
	x := 42

	// str() returns string representation of the integer
	println(x.str()) // "42"

	// hex() returns hexadecimal representation without prefix
	println(x.hex()) // "2a"

	// hex2() returns hexadecimal representation with "0x" prefix
	println(x.hex2()) // "0x2a"

	// hex_full() returns hexadecimal representation with full width padding for the type (8 digits for 32-bit int)
	println(x.hex_full()) // "0000002a"
}

Float Methods

Float Methods

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Float Methods in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of float methods.

v
Run
module main

fn main() {
	f := 12345.6789

	// str() returns string representation of the float
	println(f.str()) // "12345.6789"

	// strg() returns string representation (often identical to str())
	println(f.strg()) // "12345.6789"

	// strlong() returns a full/long string representation of the float
	println(f.strlong()) // "12345.6789"

	// strsci(precision) returns scientific notation with specified precision/decimal places
	println(f.strsci(4)) // "1.2346e+04"

	// eq_epsilon(other) performs a comparison using machine epsilon (for near-equality)
	f2 := 12345.678900000001
	println(f.eq_epsilon(f2)) // true
}

U8 Methods

U8 Methods

In V, primitive data types are the core building blocks of the language. This section details how to declare and use U8 Methods in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of u8 methods.

v
Run
module main

fn main() {
	b := u8(65) // ASCII code for 'A'

	// str() returns string representation of the numeric value
	println(b.str()) // "65"

	// ascii_str() returns string of length 1 containing the character
	println(b.ascii_str()) // "A"

	// hex() returns hexadecimal representation
	println(b.hex()) // "41"

	// hex_full() returns hexadecimal representation (same as hex() for u8)
	println(b.hex_full()) // "41"

	// is_alnum() checks if the character is alphanumeric
	println(b.is_alnum()) // true

	// is_bin_digit() checks if the character is a binary digit ('0' or '1')
	println(b.is_bin_digit()) // false

	// is_capital() checks if the character is an uppercase letter
	println(b.is_capital()) // true

	// is_digit() checks if the character is a decimal digit ('0'-'9')
	println(b.is_digit()) // false

	// is_hex_digit() checks if the character is a hexadecimal digit ('0'-'9', 'a'-'f', 'A'-'F')
	println(b.is_hex_digit()) // true

	// is_letter() checks if the character is an alphabetic letter
	println(b.is_letter()) // true

	// is_oct_digit() checks if the character is an octal digit ('0'-'7')
	println(b.is_oct_digit()) // false

	// is_space() checks if the character is a whitespace character
	println(b.is_space()) // false

	// repeat(count) repeats the character count times and returns a string
	println(b.repeat(3)) // "AAA"

	// str_escaped() returns an escaped string representation of the character
	println(b.str_escaped()) // "A"
}

Size Pointer Methods

Size Pointer Methods

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Size Pointer Methods in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of size and pointer methods.

v
Run
module main

fn main() {
	// isize and usize methods
	sz := isize(100)
	usz := usize(200)

	// str() returns string representation
	println(sz.str())  // "100"
	println(usz.str()) // "200"

	// voidptr methods
	x := 42
	p := voidptr(&x)

	// str() returns the memory address as string
	println(p.str().starts_with('0x')) // true

	// hex_full() returns full-width hex representation of address
	println(p.hex_full().len > 0) // true

	// vbytes(len) returns a byte array representation of the memory pointed to (must be called in unsafe block)
	unsafe {
		bytes := p.vbytes(int(sizeof(int)))
		println(bytes) // [42, 0, 0, 0]
	}
}

Rune Type

Declare Rune

Declare Rune

A rune in V represents a single Unicode code point. Runes are declared using backticks (e.g., \a\, \🔥\) and are represented internally as 32-bit unsigned integers (u32). This allows V to support multi-byte Unicode characters (like emojis or Chinese characters) as single character tokens.

This example shows how to declare and print runes.

Additional Context from Repository docs:

This example demonstrates the concepts of declare rune.

v
Run
fn main() {
	initial := `A`
	symbol := `🔥`

	println(typeof(initial).name)
	println(typeof(symbol).name)
	println('Initial: ${initial.str()}')
	println('Symbol: ${symbol.str()}')
}

Rune Operations With Strings

Rune Operations With Strings

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Rune Operations With Strings in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of rune operations with strings.

v
Run
fn main() {
	beverage := 'café'
	s := `é`
	// declare rune
	println(beverage.count(s.str()))
	// 1
}

Rune Methods

Rune Methods

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Rune Methods in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of rune methods.

v
Run
module main

fn main() {
	r := `A`

	// bytes() returns the byte representation (UTF-8 bytes) of the rune
	println(r.bytes()) // [65]

	// hex() returns the hexadecimal representation of the rune code point
	println(r.hex()) // "41"

	// length_in_bytes() returns the size of the rune in bytes (1 to 4)
	println(r.length_in_bytes()) // 1

	// repeat(count) returns a string with the rune repeated count times
	println(r.repeat(3)) // "AAA"

	// str() returns the string representation of the rune
	println(r.str()) // "A"

	// to_lower() returns the lowercase rune
	println(r.to_lower().str()) // "a"

	// to_upper() returns the uppercase rune
	println(r.to_upper().str()) // "A"

	// to_title() returns the titlecase rune
	println(r.to_title().str()) // "A"

	// Testing with a multi-byte UTF-8 rune (dog emoji 🐕)
	r2 := `🐕`
	println(r2.bytes())           // [240, 159, 144, 149]
	println(r2.hex())             // "1f415" (Unicode code point in hex)
	println(r2.length_in_bytes()) // 4
	println(r2.repeat(2))         // "🐕🐕"
	println(r2.str())             // "🐕"
}

String Type

Declare String

Declare String

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Declare String in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of declare string.

v
Run
fn main() {
	greeting := 'hello'
	name := 'Ada'
	message := greeting + ', ' + name + '!'

	println(message)
	println(message.len)
	println(typeof(message).name)
}

String Read Only Array Of Bytes

String Read Only Array Of Bytes

In V, a string is internally represented as a read-only array of bytes (u8). This means you can access individual bytes of a string using array indexing (str[index]), but you cannot change them.

This example shows how to read bytes from a string and print their values.

Additional Context from Repository docs:

This example demonstrates the concepts of string read only array of bytes.

v
Run
fn main() {
	fruit := 'Orange'
	println(typeof(fruit[0]).name)
	// byte
	println(fruit[0])
	// 79
}

Strings Immutable By Default

Strings Immutable By Default

Strings in V are completely immutable. Once a string is created, its characters cannot be modified in place. Any operation that manipulates a string (such as replacing characters or converting to uppercase) returns a brand new string instead of modifying the original.

This example demonstrates that strings are read-only and cannot be changed.

Additional Context from Repository docs:

This example demonstrates the concepts of strings immutable by default.

v
Run
fn main() {
	s := 'hello'
	// variable s is immutable
	s = 'Hello!'
	// this results in error
}

Declaring Mutable Strings

Declaring Mutable Strings

While strings are immutable, you can declare a mutable string variable using mut. This allows the variable to be reassigned to a new string value, or appended to using the += operator.

This example shows how to declare a mutable string and append text to it.

Additional Context from Repository docs:

This example demonstrates the concepts of declaring mutable strings.

v
Run
fn main() {
	mut msg := 'Hello Friend!'
	msg = 'Hope you are doing good.'
	println(msg)
	// Hope you are doing good.
	msg = msg + ' There is a surprise for you.'
	println(msg)
	// Hope you are doing good. There is a surprise for you.
}

Cannot Mutate String Elements

Cannot Mutate String Elements

Even if a string variable is declared with mut, you cannot mutate its individual characters or bytes directly via index assignment (e.g., s[0] = \a\``). The compiler will throw an error to protect string integrity.

This program shows that element mutation is strictly forbidden.

Additional Context from Repository docs:

This example demonstrates the concepts of cannot mutate string elements.

v
Run
fn main() {
	mut greet := 'good Day'

	greet[0] = 'G' // this results in error
}

String Interpolation

String Interpolation

String interpolation is a clean way to insert variables or expressions inside a string literal. In V, you do this by wrapping the variable or expression in ${variable} inside a single-quoted string.

This example demonstrates how to format strings with variable values.

Additional Context from Repository docs:

This example demonstrates the concepts of string interpolation.

v
Run
fn main() {
	a := 'coding'
	b := 'fun'
	println('${a} is ${b}')
	println('${a} is ${b}')
}

Escape Special Characters

Escape Special Characters

V strings support standard escape characters (like \n for newlines, \t for tabs, and \\ for backslashes) to represent special characters inside a string literal.

This example shows how these escape sequences are rendered in the console.

Additional Context from Repository docs:

This example demonstrates the concepts of escape special characters.

v
Run
module main

fn main() {
	// 1. Newline escape character (\n)
	println('Hello\nWorld!')

	// 2. Tab escape character (\t)
	println('Name:\tAlice\tAge:\t25')

	// 3. Backslash escape character (\\)
	println('File path: C:\\Program Files\\V')

	// 4. Escaping single quotes (\') in a single-quoted string
	println('It\'s my Daughter\'s birthday!')

	// 5. Escaping double quotes (\") in a double-quoted string
	println("She said, \"V is fast!\"")
}

Declare Raw Strings

Declare Raw Strings

If you want to write a string literal where escape sequences (like \n) are treated as literal text instead of special commands, you can declare a raw string by prefixing the string literal with r (e.g., r\'hello\nworld\').

This is extremely useful when writing regular expressions or file paths.

Additional Context from Repository docs:

This example demonstrates the concepts of declare raw strings.

v
Run
module main

fn main() {
	i := r'hi \how are you/?'
	println(i)
}

String Concatenation Using Plus Sign

String Concatenation Using Plus Sign

In V, primitive data types are the core building blocks of the language. This section details how to declare and use String Concatenation Using Plus Sign in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of string concatenation using plus sign.

v
Run
module main

fn main() {
	a := 'con'
	b := 'cat'
	println(a + b)
	// concat
}

String Concatenation Using Interpolation

String Concatenation Using Interpolation

In V, primitive data types are the core building blocks of the language. This section details how to declare and use String Concatenation Using Interpolation in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of string concatenation using interpolation.

v
Run
module main

fn main() {
	i := 1
	j := 'man army'
	println('${i} ${j}')
}

Extract Substring From String Literal

Extract Substring From String Literal

V provides two main techniques to extract substrings from a string literal or variable:

  1. The .substr(start, end) Method: Takes the starting index (inclusive) and ending index (exclusive) as parameters.
  2. Range Slicing Syntax [start..end]: A clean and idiomatic syntax (similar to Go and Rust) where you specify range offsets. If the starting index is omitted (e.g. [..end]), it defaults to 0. If the ending index is omitted (e.g. [start..]), it defaults to the length of the string.

Both techniques are demonstrated in the example below.

Additional Context from Repository docs:

This example demonstrates the concepts of extract substring from string literal.

v
Run
module main

fn main() {
	a := 'Camel'

	// Method 1: Using the substr(start, end) method
	// Extracts characters from index 0 up to (but not including) index 3
	b := a.substr(0, 3)
	println(b) // Output: Cam

	// Method 2: Using idiomatic range slicing syntax [start..end] (similar to Go/Rust)
	// Slices from index 1 up to (but not including) index 4
	c := a[1..4]
	println(c) // Output: ame

	// Method 3: Slicing from start to index [..end]
	// If the start index is omitted, it defaults to 0
	d := a[..3]
	println(d) // Output: Cam

	// Method 4: Slicing from index to end [start..]
	// If the end index is omitted, it defaults to the string length
	e := a[2..]
	println(e) // Output: mel
}

Split String

Split String

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Split String in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of split string.

v
Run
module main

fn main() {
	sp := 'The tiny tiger tied the tie tighter to its tail'
	res := sp.split(' ')
	// split by space as delimiter
	println(typeof(res).name)
	// []string
	println(res)
	// ['The', 'tiny', 'tiger', 'tied', 'the', 'tie', 'tighter', 'to', 'its', 'tail']
}

String To Runes Array

String To Runes Array

In V, primitive data types are the core building blocks of the language. This section details how to declare and use String To Runes Array in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of string to runes array.

v
Run
module main

fn main() {
	doge_moon := '🐕+🚀=🌑'
	doge_moon_runes := doge_moon.runes()
	println(doge_moon_runes)
	println(typeof(doge_moon_runes).name) // []rune
}

Count Sub String Occurences

Count Sub String Occurences

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Count Sub String Occurences in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of count sub string occurences.

v
Run
module main

fn main() {
	sp := 'The tiny tiger tied the tie tighter to its tail'
	println(sp.count('t'))
	// 10
	println(sp.count('T'))
	// 1
	println(sp.count('tie'))
	// 2
	println(sp.count('-'))
	// 0
}

Check String Contains Substring

Check String Contains Substring

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Check String Contains Substring in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of check string contains substring.

v
Run
module main

fn main() {
	hs := 'monday'

	if hs.contains('mon') {
		println('${hs} contains mon')
	} else {
		println('${hs} does not contains mon')
	}
}

String Contains Is Case Sensitive

String Contains Is Case Sensitive

In V, primitive data types are the core building blocks of the language. This section details how to declare and use String Contains Is Case Sensitive in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of string contains is case sensitive.

v
Run
module main

fn main() {
	hs := 'Monday'

	if hs.contains('mon') {
		println('${hs} contains mon')
	} else {
		println('${hs} does not contains mon')
	}
}

Common String Methods

Common String Methods

In V, primitive data types are the core building blocks of the language. This section details how to declare and use Common String Methods in a simple, straightforward manner. Beginners should pay close attention to how variables of this type are initialized and how built-in methods are called on them.

Additional Context from Repository docs:

This example demonstrates the concepts of common string methods.

v
Run
module main

fn main() {
	s := '  Hello, V!  '

	// to_lower() returns the lowercase version of the string
	println(s.to_lower()) // "  hello, v!  "

	// to_upper() returns the uppercase version of the string
	println(s.to_upper()) // "  HELLO, V!  "

	// trim_space() trims leading and trailing whitespaces
	println(s.trim_space()) // "Hello, V!"

	// trim(cutset) trims leading and trailing characters that match any character in the cutset
	println(s.trim(' ')) // "Hello, V!"

	// replace(old, new) replaces all occurrences of old with new
	println(s.replace('V', 'World')) // "  Hello, World!  "

	// replace_once(old, new) replaces the first occurrence of old with new
	println(s.replace_once('l', 'x')) // "  Hexlo, V!  "

	// index(sub) returns the start index of the first occurrence of sub as an optional ?int
	idx := s.index('Hello') or { -1 }
	println(idx) // 2

	// last_index(sub) returns the start index of the last occurrence of sub as an optional ?int
	last_idx := s.last_index('l') or { -1 }
	println(last_idx) // 5

	// starts_with(prefix) checks if the string starts with the prefix
	println(s.starts_with('  ')) // true

	// ends_with(suffix) checks if the string ends with the suffix
	println(s.ends_with('!')) // false (ends with spaces)

	// is_pure_ascii() checks if all characters in the string are pure ASCII
	println(s.is_pure_ascii()) // true

	// split_into_lines() splits a string into an array of lines
	multiline := "line 1\nline 2"
	println(multiline.split_into_lines()) // ["line 1", "line 2"]

	// split_by_space() splits a string by space as delimiter
	println(s.split_by_space()) // ["Hello,", "V!"]
}

Chapter 4 Control Flow

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:

Control Flow Extras


Control flow determines the execution path of your code. In this chapter, we cover conditionals (if-else), pattern matching (match), and the versatile for loop. V simplifies control flow by using fewer keywords, making code highly readable.

Control Flow Extras

Chaining Else If

Chaining Else If

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of Chaining Else If in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of chaining else if.

v
Run
module main

fn breakfast_menu(day string) {
	if day == 'Monday' {
		println('Bread, Jam, Half boiled Egg')
	} else if day == 'Tuesday' {
		println('Bread, Jam, Juice')
	} else if day == 'Wednesday' {
		println('Milk, Bread, Fruit Bowl')
	} else if day == 'Thursday' {
		println('Bread, Jam, Juice')
	} else if day == 'Friday' {
		println('Cereals, Bread, Jam, Half boiled Egg')
	} else if day == 'Saturday' {
		println('Milk, Bread, Fruit Bowl')
	} else if day == 'Sunday' {
		println('Cereals, Bread, Jam, Half boiled Egg')
	} else {
		println('invalid input')
	}
}

fn main() {
	breakfast_menu('Saturday')
}

If With Goto

If With Goto

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of If With Goto in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of if with goto.

v
Run
module main

import os

fn main() {
	improper_input_age:
	println('Invalid input. Please provide value greater than 0.')

	next_person:
	inp := os.input('Enter your age:')

	if inp != 'stop' {
		age := inp.int()

		if age >= 13 {
			println('You are allowed to watch this movie')
		} else if age > 0 && age < 13 {
			println('Parental Guidance is required to watch this movie')
		} else if age <= 0 {
			unsafe {
				goto improper_input_age
			}
		}
		unsafe {
			goto next_person
		}
	}
}

Cascade Match Conditions

Cascade Match Conditions

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of Cascade Match Conditions in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of cascade match conditions.

v
Run
module main

fn breakfast_menu(day string) string {
	return match day {
		'Monday' {
			'Bread, Jam, Half boiled Egg'
		}
		'Tuesday', 'Thursday' {
			'Bread, Jam, Juice'
		}
		'Wednesday' {
			'Milk, Bread, Fruit Bowl'
		}
		'Friday', 'Sunday' {
			'Cereals, Bread, Jam, Half boiled Egg'
		}
		'Saturday' {
			'Milk, Bread, Fruit Bowl'
		}
		else {
			'invalid input'
		}
	}
}

fn main() {
	friday_menu := breakfast_menu('Friday')
	println(friday_menu)

	sunday_menu := breakfast_menu('Sunday')
	println(sunday_menu)

	tuesday_menu := breakfast_menu('Tuesday')
	println(tuesday_menu)

	thursday_menu := breakfast_menu('Thursday')
	println(thursday_menu)
}

Match As Switch Case

Match As Switch Case

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of Match As Switch Case in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of match as switch case.

v
Run
module main

fn breakfast_menu(day string) {
	match day {
		'Monday' { println('Bread, Jam, Half boiled Egg') }
		'Tuesday' { println('Bread, Jam, Juice') }
		'Wednesday' { println('Milk, Bread, Fruit Bowl') }
		'Thursday' { println('Bread, Jam, Juice') }
		'Friday' { println('Cereals, Bread, Jam, Half boiled Egg') }
		'Saturday' { println('Milk, Bread, Fruit Bowl') }
		'Sunday' { println('Cereals, Bread, Jam, Half boiled Egg') }
		else { println('invalid input') }
	}
}

fn main() {
	breakfast_menu('Sunday')
}

Match Pattern Matching

Match Pattern Matching

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of Match Pattern Matching in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of match pattern matching.

v
Run
module main

fn main() {
	age := 18
	res := match age {
		0...18 { 'Person with age $age classified as a Child' }
		19...120 { 'Person with age $age classified as an Adult' }
		else { '$age is must be in the range 0 to 120' }
	}
	println(res)
}

Match With Enum

Match With Enum

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of Match With Enum in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of match with enum.

v
Run
module main

enum Day {
	sunday
	monday
	tuesday
	wednesday
	thursday
	friday
	saturday
}

fn breakfast_menu(day Day) string {
	return match day {
		.monday {
			'Bread, Jam, Half boiled Egg'
		}
		.tuesday, .thursday {
			'Bread, Jam, Juice'
		}
		.wednesday {
			'Milk, Bread, Fruit Bowl'
		}
		.friday, .sunday {
			'Cereals, Bread, Jam, Half boiled Egg'
		}
		.saturday {
			'Milk, Bread, Fruit Bowl'
		}
	}
}

fn main() {
	friday_menu := breakfast_menu(Day.friday)
	println(friday_menu)

	sunday_menu := breakfast_menu(Day.sunday)
	println(sunday_menu)

	tuesday_menu := breakfast_menu(Day.tuesday)
	println(tuesday_menu)

	thursday_menu := breakfast_menu(Day.thursday)
	println(thursday_menu)
}

Match With Enum And Else

Match With Enum And Else

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of Match With Enum And Else in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of match with enum and else.

v
Run
module main

enum Day {
	sunday
	monday
	tuesday
	wednesday
	thursday
	friday
	saturday
}

fn weekend_breakfast_menu(day Day) string {
	return match day {
		.sunday {
			'Cereals, Bread, Jam, Half boiled Egg'
		}
		.saturday {
			'Milk, Bread, Fruit Bowl'
		}
		else {
			'Sorry, we are closed on weekdays!'
		}
	}
}

fn main() {
	sunday_menu := weekend_breakfast_menu(Day.sunday)
	println(sunday_menu)

	tuesday_menu := weekend_breakfast_menu(Day.tuesday)
	println(tuesday_menu)
}

Bare For

Bare For

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of Bare For in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of bare for.

v
Run
module main

fn main() {
	mut count := 1
	for {
		println('Hi $count times')
		count += 1
	}
}

Break For

Break For

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of Break For in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of break for.

v
Run
module main

import os

fn main() {
	mut count := 0
	input := os.input('Enter number of times to Greet:')
	limit := input.int()
	for {
		if count >= limit {
			break
		}
		println('Hi')
		count += 1
	}
	println('Greeted Hi $count times')
}

Continue For

Continue For

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of Continue For in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of continue for.

v
Run
module main

fn main() {
	for i in 0 .. 10 {
		if i % 2 == 0 { // skips printing number that is a multiple of 2
			continue
		}
		println(i)
	}
}

For C Style

For C Style

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of For C Style in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of for c style.

v
Run
module main

fn main() {
	sample := [3, 4, 23, 12, 4, 1, 45, 12, 42, 17, 92, 38]
	for i := 0; i < sample.len; i += 3 {
		println(sample[i])
	}
}

For On Array Without Index

For On Array Without Index

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of For On Array Without Index in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of for on array without index.

v
Run
module main

fn main() {
	col := [1, 2, 3, 4, 5, 6, 7]
	for val in col {
		if val % 2 == 0 {
			println('$val is Even')
		} else {
			println('$val is Odd')
		}
	}
}

For On Arrays

For On Arrays

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of For On Arrays in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of for on arrays.

v
Run
module main

fn main() {
	fruits := ['apple', 'banana', 'coconut']
	for idx, ele in fruits {
		println('idx: $idx \t fruit: $ele')
	}
}

For On Maps

For On Maps

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of For On Maps in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of for on maps.

v
Run
module main

fn main() {
	lottery := {
		'First':       1000
		'Second':      700
		'Consolation': 200
	}

	for k, v in lottery {
		println('$k prize lottery amount: $v')
	}
}

For On Maps Ignore Key

For On Maps Ignore Key

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of For On Maps Ignore Key in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of for on maps ignore key.

v
Run
module main

fn main() {
	basket := {
		'apples':  10
		'bananas': 12
	}

	mut total := 0
	for _, v in basket {
		total += v
	}
	println('Total number of fruits: $total')
}

For On Range

For On Range

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of For On Range in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of for on range.

v
Run
module main

fn main() {
	for val in 0 .. 4 {
		println(val)
	}
}

For With Continue Break And Labels

For With Continue Break And Labels

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of For With Continue Break And Labels in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of for with continue break and labels.

v
Run
module main

import os

fn main() {
	input := os.input('Enter the number of multiplication tables to print:')
	limit := input.int()
	if limit <= 0 {
		return
	}
	first_loop: for i := 1; i <= 10; i++ {
		println('Printing multiplication table for $i')
		for j := 1; j <= 10; j++ {
			mul := i * j
			println('$i * $j = $mul')
			if mul >= limit * 10 {
				break first_loop
			}
		}
		println('*********')
	}
}

Reverse For

Reverse For

Control flow structures allow your program to decide which path of execution to take. This example demonstrates the usage of Reverse For in V, showing how to control execution paths cleanly and safely.

Additional Context from Repository docs:

This example demonstrates the concepts of reverse for.

v
Run
module main

fn main() {
	subjects := ['zoology', 'chemistry', 'physics', 'algebra']

	for i := subjects.len - 1; i >= 0; i-- {
		println(subjects[i])
	}
}

Chapter 5 Collections: Arrays and Maps

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:

Arrays

Maps


Collections allow you to group multiple data items together. V provides two primary built-in collection types: arrays (ordered lists of elements) and maps (key-value dictionaries). This chapter covers creating, accessing, and manipulating these collections using modern functional patterns like map and filter.

Arrays

Declare And Initialize

Declare And Initialize

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of declare and initialize.

v
Run
fn main() {
	mut sports := ['cricket', 'hockey', 'football']
	println(sports)
}

Declare Empty Array

Declare Empty Array

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of declare empty array.

v
Run
fn main() {
	mut animals := []string{}
	println(animals)
	// prints empty array: []
	animals << 'Chimpanzee'
	animals << 'Dog'
	println(animals)
	// ['Chimpanzee', 'Dog']
}

Declare Array With Len

Declare Array With Len

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of declare array with len.

v
Run
fn main() {
	mut i := []int{len: 3}
	println(i)
}

Declare Array With Init And Len

Declare Array With Init And Len

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of declare array with init and len.

v
Run
fn main() {
	mut j := []int{len: 3, init: 1}
	println(j)
}

Declare Array With Cap

Declare Array With Cap

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of declare array with cap.

v
Run
fn main() {
	mut k := []int{cap: 2}
	println(k)
}

Working With Array Properties

Working With Array Properties

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of working with array properties.

v
Run
fn main() {
	mut sports := ['cricket', 'hockey', 'football']
	println(sports.len)
	// Length of sports array
	println(sports.cap)
	// Capacity of sports array
	println('----Deleting football----')
	sports.delete(2)
	// deleting football
	println('Length of sports array: ${sports.len}')
	println('Capacity of sports array: ${sports.cap}')

	println('----Adding volleyball and baseball----')

	sports << ['volleyball', 'baseball']
	println(sports)
	println('Length of sports array: ${sports.len}')
	println('Capacity of sports array: ${sports.cap}')
}

Access Array Elements Using Index

Access Array Elements Using Index

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of access array elements using index.

v
Run
fn main() {
	mut sports := ['cricket', 'hockey', 'football']
	s := sports[1]
	println(s) // hockey
}

Access Array Elements Using Slices

Access Array Elements Using Slices

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of access array elements using slices.

v
Run
fn main() {
	mut sports := ['cricket', 'hockey', 'football']
	println(sports[1..3])
}

In Operator With Array

In Operator With Array

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of in operator with array.

v
Run
fn main() {
	odd := [1, 3, 5, 7]

	println(3 in odd)
	// prints: true
	println(8 !in odd)
	// prints: true
}

Append Array

Append Array

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of append array.

v
Run
fn main() {
	mut even := [2, 4, 6]
	even << 8
	println(even)
	// prints [2, 4, 6, 8]
	even << [10, 12, 14]
	println(even)
	// prints: [2, 4, 6, 8, 10, 12, 14]
}

Define Fixed Size Array

Define Fixed Size Array

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of define fixed size array.

v
Run
fn main() {
	mut fix := [4]int{}
	println(fix)
	// [0, 0, 0, 0]
}

Update Fixed Size Array Elements

Update Fixed Size Array Elements

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of update fixed size array elements.

v
Run
fn main() {
	mut fix := [4]int{}
	fix[1] = 33
	println(fix)
	//[0, 33, 0, 0]
}

Determining Type Of Fixed Array

Determining Type Of Fixed Array

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of determining type of fixed array.

v
Run
fn main() {
	mut fix := [4]int{}
	println(typeof(fix).name) // [4]int
}

Slicing Fixed Size Array Results In Ordinary Array

Slicing Fixed Size Array Results In Ordinary Array

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of slicing fixed size array results in ordinary array.

v
Run
fn main() {
	mut fix := [4]int{}
	fix[1] = 33
	s := fix[1..]
	println(s)
	// [33, 0, 0]
	println(typeof(s).name) // prints: []int
}

Declaring Multi Dimensional Arrays

Declaring Multi Dimensional Arrays

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of declaring multi dimensional arrays.

v
Run
fn main() {
	mut coordinates_2d := [][]int{len: 4, init: []int{len: 2}}
	println(typeof(coordinates_2d).name)
	// [][]int
	println(coordinates_2d)
	// [[0, 0], [0, 0], [0, 0], [0, 0]]
}

Updating Multi Dimensional Array Indices

Updating Multi Dimensional Array Indices

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of updating multi dimensional arrays.

v
Run
fn main() {
	mut coordinates_2d := [][]int{len: 4, init: []int{len: 2}}
	println(coordinates_2d.len)

	point_1 := [0, 0]
	point_2 := [0, 1]
	point_3 := [1, 0]
	point_4 := [1, 1]

	coordinates_2d[0] = point_1
	coordinates_2d[1] = point_2
	coordinates_2d[2] = point_3
	coordinates_2d[3] = point_4
	println(coordinates_2d)
}

Reassigning Multi Dimensional Arrays

Reassigning Multi Dimensional Arrays

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of updating multi dimensional arrays.

v
Run
fn main() {
	mut coordinates_2d := [][]int{len: 4, init: []int{len: 2}}
	println(coordinates_2d.len)
	coordinates_2d = [
		[0, 0],
		[0, 1],
		[1, 0],
		[1, 1],
	]
	println(coordinates_2d)
}

Clone Array

Clone Array

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of clone array.

v
Run
fn main() {
	r := [1, 2, 3, 4]
	mut u := r.clone()
	// copies the array r to u
	println(u)
}

Copy Array

Copy Array

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of copy array.

v
Run
fn main() {
	r := [1, 2, 3, 4]
	s := unsafe { r }
	println(s)
	unsafe {
		r.free()
	}
}

Sort Integer Array

Sort Integer Array

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of sort integer array.

v
Run
fn main() {
	mut i := [3, 2, 8, 1]
	i.sort()
	// ascending order
	println(i)
	i.sort(a > b)
	// descending order
	println(i)
}

Sort String Array

Sort String Array

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of sort string array.

v
Run
fn main() {
	mut fruits := ['Apples', 'avocado', 'banana', 'Orange']

	fruits.sort()
	// ascending order
	println(fruits)
	fruits.sort(a > b)
	// reverse order
	println(fruits)
}

Sort Struct Array

Sort Struct Array

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of sort struct array.

v
Run
module main

struct Student {
	id    int
	name  string
	class int
}

fn main() {
	// Declare an empty array
	mut students := []Student{}

	// Create students
	st1 := Student{
		id:    1
		name:  'Ram'
		class: 9
	}
	st2 := Student{
		id:    2
		name:  'Katy'
		class: 3
	}
	st3 := Student{
		id:    3
		name:  'Tom'
		class: 6
	}

	// Append all the students to the array
	students << [st1, st2, st3]
	println(students)

	// Reverse Sort students by id
	students.sort(a.id > b.id)

	println('Students sorted in reverse order of id:')
	println(students)

	// Sort students by class in ascending order
	students.sort(a.class < b.class)

	println('Students sorted in ascending order of class:')
	println(students)

	// Sort students by name in reverse order
	students.sort(a.name > b.name)

	println('Students sorted in reverse order of name:')
	println(students)
}

Filter Array

Filter Array

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of filter array.

v
Run
fn main() {
	f := [1, 2, 3, 4, 5, 6, 7, 8, 9]
	multiples_of_3 := f.filter(it % 3 == 0)
	println(multiples_of_3)
	// [3, 6, 9]
}

Filter With Anonymous Funcs On Array

Filter With Anonymous Funcs On Array

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of filter with anonymous funcs on array.

v
Run
fn main() {
	fruits := ['apple', 'mango', 'water melon', 'musk melon']

	fruits_starting_m := fruits.filter(fn (f string) bool {
		return f.starts_with('m')
	})

	println(fruits_starting_m)
}

Map Array Items

Map Array Items

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of map array items.

v
Run
fn main() {
	visitor := ['Tom', 'Ram', 'Rao']
	res := visitor.map('Mr. ' + it)
	println(res)
}

Map Using Anonymous Funcs On Array

Map Using Anonymous Funcs On Array

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of map using anonymous funcs on array.

v
Run
fn main() {
	colors := ['red', 'blue', 'green', 'white', 'black']

	colors_with_letter_e := colors.map(fn (c string) int {
		if c.contains('e') { return 1 } else { return 0 }
	})

	println(colors_with_letter_e)
}

Array Methods

Array Methods

An array is a collection of elements of the same type. In V, arrays are declared using square brackets. They are index-based, dynamically sized, and provide built-in methods like map(), filter(), and sort() for functional-style manipulation.

These examples show how to initialize, append, clone, copy, and manipulate arrays.

Additional Context from Repository docs:

This example demonstrates the concepts of array methods.

v
Run
module main

// A custom comparison function for sorting.
// It accepts references to elements (e.g. &int) and returns -1, 1, or 0.
fn compare_ints(a &int, b &int) int {
	val_a := *a
	val_b := *b
	if val_a < val_b { return -1 }
	if val_a > val_b { return 1 }
	return 0
}

fn main() {
	println('--- Array Built-in Methods ---')

	// 1. ensure_cap(required)
	// Ensures that the array has at least the specified capacity.
	mut a := [10, 20, 30]
	a.ensure_cap(10)
	println('ensure_cap: cap is ${a.cap >= 10}') // true

	// 2. repeat(count)
	// Repeats the array count times and returns a new array.
	rep := a.repeat(2)
	println('repeat: ${rep}') // [10, 20, 30, 10, 20, 30]

	// 3. repeat_to_depth(count, depth) (unsafe)
	// Recursively repeats a multi-dimensional array count times to the specified depth.
	grid := [[1, 2], [3, 4]]
	unsafe {
		rep_grid := grid.repeat_to_depth(2, 1)
		// Cast the raw array struct back to typed [][]int
		typed_grid := *(&[][]int(&rep_grid))
		println('repeat_to_depth: ${typed_grid}') // [[1, 2], [3, 4], [1, 2], [3, 4]]
		rep_grid.free()
	}

	// 4. insert(index, val)
	// Inserts a new element at the specified index.
	a.insert(1, 15)
	println('insert: ${a}') // [10, 15, 20, 30]

	// 5. prepend(val)
	// Prepends a new element at the beginning of the array.
	a.prepend(5)
	println('prepend: ${a}') // [5, 10, 15, 20, 30]

	// 6. delete(index)
	// Deletes the element at the specified index.
	a.delete(1) // Deletes index 1 (which is 10)
	println('delete: ${a}') // [5, 15, 20, 30]

	// 7. delete_many(index, size)
	// Deletes size elements starting from the specified index.
	a.delete_many(1, 2) // Deletes 2 elements starting at index 1
	println('delete_many: ${a}') // [5, 30]

	// 8. clear()
	// Sets the array length to 0, retaining capacity.
	mut a_clear := [1, 2, 3]
	a_clear.clear()
	println('clear: len is ${a_clear.len}') // 0

	// 9. reset() (unsafe)
	// Sets all elements of the array to 0 / empty values without altering len or cap.
	mut a_reset := [1, 2, 3]
	unsafe {
		a_reset.reset()
	}
	println('reset: ${a_reset}') // [0, 0, 0]
	unsafe {
		a_reset.free()
	}

	// 10. trim(index)
	// Truncates the array length to index.
	mut a_trim := [1, 2, 3, 4]
	a_trim.trim(2)
	println('trim: ${a_trim}') // [1, 2]

	// 11. drop(num)
	// Drops the first num elements in-place.
	mut a_drop := [1, 2, 3, 4]
	a_drop.drop(2)
	println('drop: ${a_drop}') // [3, 4]

	// 12. first()
	// Returns the first element of the array.
	println('first: ${a_drop.first()}') // 3

	// 13. last()
	// Returns the last element of the array.
	println('last: ${a_drop.last()}') // 4

	// 14. pop_left()
	// Removes and returns the first element of the array.
	mut a_pop := [1, 2, 3]
	first_val := a_pop.pop_left()
	println('pop_left: value = ${first_val}, array = ${a_pop}') // 1, [2, 3]

	// 15. pop()
	// Removes and returns the last element of the array.
	last_val := a_pop.pop()
	println('pop: value = ${last_val}, array = ${a_pop}') // 3, [2]

	// 16. delete_last()
	// Deletes the last element of the array.
	mut a_del_last := [1, 2, 3]
	a_del_last.delete_last()
	println('delete_last: ${a_del_last}') // [1, 2]

	// 17. clone()
	// Returns a deep copy of the array.
	a_clone := a_del_last.clone()
	println('clone: ${a_clone}') // [1, 2]

	// 18. clone_to_depth(depth) (unsafe)
	// Recursively clones a multi-dimensional array up to the specified depth.
	grid2 := [[1, 2], [3, 4]]
	unsafe {
		grid_clone := grid2.clone_to_depth(1)
		typed_clone := *(&[][]int(&grid_clone))
		println('clone_to_depth: ${typed_clone}') // [[1, 2], [3, 4]]
		grid_clone.free()
	}

	// 19. push_many(val, size) (unsafe)
	// Appends size elements starting from a raw pointer val to the array.
	mut a_push := [1, 2]
	vals := [3, 4]
	unsafe {
		a_push.push_many(vals.data, 2)
	}
	println('push_many: ${a_push}') // [1, 2, 3, 4]
	unsafe {
		a_push.free()
		vals.free()
	}

	// 20. reverse()
	// Returns a new reversed copy of the array.
	a_rev := [1, 2, 3]
	println('reverse: ${a_rev.reverse()}') // [3, 2, 1]

	// 21. reverse_in_place()
	// Reverses the array elements in-place.
	mut a_rev_ip := [1, 2, 3]
	a_rev_ip.reverse_in_place()
	println('reverse_in_place: ${a_rev_ip}') // [3, 2, 1]

	// 22. free() (unsafe)
	// Deallocates the array's buffer.
	mut a_free := [1, 2, 3]
	unsafe {
		a_free.free()
	}
	println('free: array freed')

	// 23. filter(it)
	// Filters elements that satisfy a predicate using compiler-defined `it` expression.
	a_filt := [1, 2, 3, 4]
	filtered := a_filt.filter(it % 2 == 0)
	println('filter: ${filtered}') // [2, 4]

	// 24. any(it)
	// Checks if any element satisfies the predicate.
	println('any: ${a_filt.any(it > 3)}') // true

	// 25. count(it)
	// Counts how many elements satisfy the predicate.
	println('count: ${a_filt.count(it % 2 == 0)}') // 2

	// 26. all(it)
	// Checks if all elements satisfy the predicate.
	println('all: ${a_filt.all(it > 0)}') // true

	// 27. map(it)
	// Maps elements to a new array using a transformation expression.
	mapped := a_filt.map(it * 10)
	println('map: ${mapped}') // [10, 20, 30, 40]

	// 28. sort() & sort(custom)
	// Sorts elements in-place. Uses optional boolean expression for custom order (uses magic vars a and b).
	mut a_sort := [3, 1, 4, 2]
	a_sort.sort()
	println('sort (default ascending): ${a_sort}') // [1, 2, 3, 4]
	a_sort.sort(a > b)
	println('sort (custom descending): ${a_sort}') // [4, 3, 2, 1]

	// 29. sorted() & sorted(custom)
	// Returns a sorted copy of the array. Uses optional boolean expression for custom order (uses magic vars a and b).
	a_sorted := [3, 1, 4, 2]
	println('sorted (default): ${a_sorted.sorted()}') // [1, 2, 3, 4]
	println('sorted (custom): ${a_sorted.sorted(a > b)}') // [4, 3, 2, 1]

	// 30. sort_with_compare(callback)
	// Sorts the array in-place using a custom comparison function.
	mut a_compare := [3, 1, 4, 2]
	a_compare.sort_with_compare(compare_ints)
	println('sort_with_compare: ${a_compare}') // [1, 2, 3, 4]

	// 31. sorted_with_compare(callback)
	// Returns a sorted copy of the array using a custom comparison function.
	a_sorted_comp := [3, 1, 4, 2]
	println('sorted_with_compare: ${a_sorted_comp.sorted_with_compare(compare_ints)}') // [1, 2, 3, 4]

	// 32. contains(value)
	// Checks if the array contains value.
	println('contains: ${a_filt.contains(3)}') // true

	// 33. index(value)
	// Returns the index of the first occurrence of value, or -1 if not found.
	println('index: ${a_filt.index(3)}') // 2

	// 34. last_index(value)
	// Returns the index of the last occurrence of value, or -1 if not found.
	a_dup := [1, 2, 3, 2]
	println('last_index: ${a_dup.last_index(2)}') // 3

	// 35. grow_cap(amount)
	// Increases the array capacity by the specified amount.
	mut a_grow := [1, 2]
	a_grow.grow_cap(10)
	println('grow_cap: cap is ${a_grow.cap >= 12}') // true

	// 36. grow_len(amount) (unsafe)
	// Increases the array length by the specified amount.
	unsafe {
		a_grow.grow_len(3)
	}
	println('grow_len: ${a_grow}') // [1, 2, 0, 0, 0]
	unsafe {
		a_grow.free()
	}

	// 37. pointers() (unsafe)
	// Returns an array of void pointers (pointers()) pointing to each element.
	a_ptrs := [10, 20]
	unsafe {
		ptrs := a_ptrs.pointers()
		println('pointers (first element): ${*(&int(ptrs[0]))}') // 10
		ptrs.free()
		a_ptrs.free()
	}
}

Maps

Explicit Map Initialization

Explicit Map Initialization

A map is an unordered collection of key-value pairs, also known as a dictionary or associative array. In V, map keys must be strings or integer types, and values can be of any type. Maps are declared using curly braces with colon separators.

These examples cover how to initialize maps, look up keys, add or delete entries, and check if a key exists.

Additional Context from Repository docs:

This example demonstrates the concepts of explicit map initialization.

v
Run
fn main() {
	mut books := map[string]int{}
	books['V on Wheels'] = 320
	books['Go for Dummies'] = 279

	println(books)
}

Short Syntax Initialization Of Map

Short Syntax Initialization Of Map

A map is an unordered collection of key-value pairs, also known as a dictionary or associative array. In V, map keys must be strings or integer types, and values can be of any type. Maps are declared using curly braces with colon separators.

These examples cover how to initialize maps, look up keys, add or delete entries, and check if a key exists.

Additional Context from Repository docs:

This example demonstrates the concepts of short syntax initialization of map.

v
Run
fn main() {
	mut student_1 := {
		'english':     90
		'mathematics': 96
		'physics':     83
		'chemistry':   89
	}
	println(student_1)
}

Count Key Value Pairs In Map

Count Key Value Pairs In Map

A map is an unordered collection of key-value pairs, also known as a dictionary or associative array. In V, map keys must be strings or integer types, and values can be of any type. Maps are declared using curly braces with colon separators.

These examples cover how to initialize maps, look up keys, add or delete entries, and check if a key exists.

Additional Context from Repository docs:

This example demonstrates the concepts of count key value pairs in map.

v
Run
fn main() {
	mut student_1 := {
		'english':     90
		'mathematics': 96
		'physics':     83
		'chemistry':   89
	}
	cnt := student_1.len
	println('There are ${cnt} key-value pairs in student_1 map')
}

Value Given Key Of Map

Value Given Key Of Map

A map is an unordered collection of key-value pairs, also known as a dictionary or associative array. In V, map keys must be strings or integer types, and values can be of any type. Maps are declared using curly braces with colon separators.

These examples cover how to initialize maps, look up keys, add or delete entries, and check if a key exists.

Additional Context from Repository docs:

This example demonstrates the concepts of value given key of map.

v
Run
fn main() {
	mut student_1 := {
		'english':     90
		'mathematics': 96
		'physics':     83
		'chemistry':   89
	}

	println(student_1['physics']) // 83
}

Value Given Non Existent Key Of Map

Value Given Non Existent Key Of Map

A map is an unordered collection of key-value pairs, also known as a dictionary or associative array. In V, map keys must be strings or integer types, and values can be of any type. Maps are declared using curly braces with colon separators.

These examples cover how to initialize maps, look up keys, add or delete entries, and check if a key exists.

Additional Context from Repository docs:

This example demonstrates the concepts of value given non existent key of map.

v
Run
fn main() {
	mut student_1 := {
		'english':     90
		'mathematics': 96
		'physics':     83
		'chemistry':   89
	}

	println(student_1['geography']) // 0
}

Handling Missing Keys In Map

Handling Missing Keys In Map

A map is an unordered collection of key-value pairs, also known as a dictionary or associative array. In V, map keys must be strings or integer types, and values can be of any type. Maps are declared using curly braces with colon separators.

These examples cover how to initialize maps, look up keys, add or delete entries, and check if a key exists.

Additional Context from Repository docs:

This example demonstrates the concepts of handling missing keys in map.

v
Run
fn main() {
	mut student_1 := {
		'english':     90
		'mathematics': 96
		'physics':     83
		'chemistry':   89
	}

	sub := 'geography'
	res := student_1[sub] or { panic('marks for subject ${sub} not yet updated') } // throws error
}

Update Value Given A Key In Map

Update Value Given A Key In Map

A map is an unordered collection of key-value pairs, also known as a dictionary or associative array. In V, map keys must be strings or integer types, and values can be of any type. Maps are declared using curly braces with colon separators.

These examples cover how to initialize maps, look up keys, add or delete entries, and check if a key exists.

Additional Context from Repository docs:

This example demonstrates the concepts of update value given a key in map.

v
Run
fn main() {
	mut student_1 := {
		'english':     90
		'mathematics': 96
		'physics':     83
		'chemistry':   89
	}
	student_1['english'] = 93
	println(student_1)
}

Delete Key Value Pair From Map

Delete Key Value Pair From Map

A map is an unordered collection of key-value pairs, also known as a dictionary or associative array. In V, map keys must be strings or integer types, and values can be of any type. Maps are declared using curly braces with colon separators.

These examples cover how to initialize maps, look up keys, add or delete entries, and check if a key exists.

Additional Context from Repository docs:

This example demonstrates the concepts of delete key value pair from map.

v
Run
fn main() {
	mut student_1 := {
		'english':     90
		'mathematics': 96
		'physics':     83
		'chemistry':   89
	}

	println('Key-Value pairs before deleting a key: ${student_1.len}')
	student_1.delete('physics')
	println('Key-Value pairs after deleting a key ${student_1.len}')
}

Map Methods

Map Methods

A map is an unordered collection of key-value pairs, also known as a dictionary or associative array. In V, map keys must be strings or integer types, and values can be of any type. Maps are declared using curly braces with colon separators.

These examples cover how to initialize maps, look up keys, add or delete entries, and check if a key exists.

Additional Context from Repository docs:

This example demonstrates the concepts of map methods.

v
Run
module main

fn main() {
	println('--- Map Built-in Methods ---')

	mut m := {
		'one': 1
		'two': 2
	}
	println('initial map: ${m}') // {"one": 1, "two": 2}

	// 1. keys()
	// Returns an array containing all keys in the map.
	println('keys: ${m.keys()}') // ["one", "two"]

	// 2. values()
	// Returns an array containing all values in the map.
	println('values: ${m.values()}') // [1, 2]

	// 3. clone()
	// Returns a deep copy of the map.
	mut m_clone := m.clone()
	println('clone: ${m_clone}') // {"one": 1, "two": 2}

	// 4. delete(key)
	// Removes a key-value pair from the map by key.
	m.delete('one')
	println('delete: ${m}') // {"two": 2}

	// 5. reserve(capacity)
	// Pre-allocates space for at least capacity elements in the map.
	m.reserve(10)
	println('reserve: reserved capacity successfully')

	// 6. clear()
	// Removes all key-value pairs from the map without deallocating data.
	m.clear()
	println('clear: len is ${m.len}') // 0

	// 7. move()
	// Moves the map contents to a new map variable and clears the original map to empty.
	mut m_move := {
		'three': 3
		'four': 4
	}
	moved := m_move.move()
	println('move (new map): ${moved}') // {"three": 3, "four": 4}
	println('move (original map): ${m_move}') // {}

	// 8. free() (unsafe)
	// Deallocates the map memory.
	mut m_free := {
		'temp': 100
	}
	unsafe {
		m_free.free()
	}
	println('free: map freed successfully')
}

Chapter 6 Functions

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:

Advanced Function Features

Function Extras


Functions let you turn repeated or complex logic into small, named building blocks. A useful mental model is: define the task, give it inputs if needed, do the work, and return a useful result. This chapter starts with simple functions and then introduces multiple returns, optional results, higher-order functions, and cleanup with defer.

Advanced Function Features

Function Returns Value Example 1

Function Returns Value Example 1

A simple function is a small helper that turns inputs into a useful result. The basic pattern is: define the function, pass in values, do some work, and return the answer. In this example, add takes two integers and returns their sum.

Additional Context from Repository docs:

This example demonstrates the concepts of function returns value example 1.

v
Run
fn add(a int, b int) int {
	return a + b
}

fn main() {
	total := add(7, 5)
	println('7 + 5 = ${total}')
}

Function Returns Value Example 2

Function Returns Value Example 2

A function does not need to print anything itself. It can build a value and hand it back to the caller. Here, say_hello returns a greeting string, and main decides how to display it.

Additional Context from Repository docs:

This example demonstrates the concepts of function returns value example 2.

v
Run
fn say_hello(name string) string {
	return 'Hello, ${name}!'
}

fn main() {
	message := say_hello('Ada')
	println(message)
}

Functions Without Return Type

Functions Without Return Type

Some functions are used for actions rather than calculations. They may print output, write files, or update state. In that case, you can leave out the return type and focus on the side effect.

Additional Context from Repository docs:

This example demonstrates the concepts of funtions without return type.

v
Run
fn print_welcome_message() {
	println('Welcome to V!')
}

fn main() {
	print_welcome_message()
}

Function With Input Arguments

Function With Input Arguments

Functions become much more useful when they accept inputs. This example uses two numbers as arguments and returns their sum, showing the classic input → process → output flow.

Additional Context from Repository docs:

This example demonstrates the concepts of function with input arguments.

v
Run
fn add(a int, b int) int {
	return a + b
}

fn main() {
	result := add(15, 27)
	println('15 + 27 = ${result}')
}

Function Return Multiple Values

Function Return Multiple Values

A function can return more than one value when those values belong together. A common pattern is returning both the main result and a related detail, such as a length or status. Here, greetandmessage_length returns both the greeting and its length.

Additional Context from Repository docs:

This example demonstrates the concepts of function return multiple values.

v
Run
fn greet_and_message_length(name string) (string, int) {
	greeting := 'Hello, ${name}!'
	return greeting, greeting.len
}

fn main() {
	greeting, length := greet_and_message_length('Navule')
	println(greeting)
	println('Length: ${length}')
}

Ignore Function Return Value

Ignore Function Return Value

Sometimes you only care about one returned value. V lets you ignore the rest with _, which keeps the code readable when you are only interested in part of the result.

Additional Context from Repository docs:

This example demonstrates the concepts of ignore function return value.

v
Run
fn greet_and_message_length(name string) (string, int) {
	greeting := 'Hello, ${name}!'
	return greeting, greeting.len
}

fn main() {
	greeting, _ := greet_and_message_length('Navule')
	println(greeting)
}

Function Calls Other Function

Function Calls Other Function

Functions can call other functions to split a bigger problem into smaller steps. This makes the code easier to understand and easier to reuse later.

Additional Context from Repository docs:

This example demonstrates the concepts of function calls other function.

v
Run
fn greet(p string) string {
	return 'Hello, ${p}!'
}

fn welcome(p string) string {
	msg := 'Nice to meet you!'
	mut g := greet(p)
	g = g + ' ${msg}'
	return g
}

fn main() {
	res := welcome('Visitor')
	println(res)
}

Example 1

Example 1

V functions support several advanced features:

  • Multiple Return Values: A function can return more than one value (often a result and an error).
  • Blank Identifier (_): Used to discard unwanted return values.
  • Defer: Schedules a block of code to run right before the function exits, which is excellent for resource cleanup.
  • Anonymous Functions & Closures: Functions defined inline that can capture variables from their outer scope.

These examples illustrate these powerful concepts.

Additional Context from Repository docs:

This example demonstrates the concepts of example 1.

v
Run
fn increment_array_items(arr []int, inc int) []int {
	mut tmp := arr.clone()
	for mut i in tmp {
		i += inc
	}
	return tmp
}

fn main() {
	a := [5, 6]

	res := increment_array_items(a, 100)

	println('a: ${a}')
	println('res: ${res}')
}

Example 2

Example 2

V functions support several advanced features:

  • Multiple Return Values: A function can return more than one value (often a result and an error).
  • Blank Identifier (_): Used to discard unwanted return values.
  • Defer: Schedules a block of code to run right before the function exits, which is excellent for resource cleanup.
  • Anonymous Functions & Closures: Functions defined inline that can capture variables from their outer scope.

These examples illustrate these powerful concepts.

Additional Context from Repository docs:

This example demonstrates the concepts of example 2.

v
Run
fn increment_array_items(mut arr []int, inc int) {
	for mut i in arr {
		i += inc
	}
}

fn main() {
	mut a := [5, 6]
	increment_array_items(mut a, 100)
	// Must specify mut keyword when sending value to mut arg of a function
	println('a: ${a}')
}

Error Script Functions

Error Script Functions

V functions support several advanced features:

  • Multiple Return Values: A function can return more than one value (often a result and an error).
  • Blank Identifier (_): Used to discard unwanted return values.
  • Defer: Schedules a block of code to run right before the function exits, which is excellent for resource cleanup.
  • Anonymous Functions & Closures: Functions defined inline that can capture variables from their outer scope.

These examples illustrate these powerful concepts.

v
Run
#!/usr/local/bin/v run

cnt := 2

for i in 0 .. cnt {
	log('iteration ${i}')
}

fn log(msg string) {
	println(msg)
}

Script Functions

Script Functions

V functions support several advanced features:

  • Multiple Return Values: A function can return more than one value (often a result and an error).
  • Blank Identifier (_): Used to discard unwanted return values.
  • Defer: Schedules a block of code to run right before the function exits, which is excellent for resource cleanup.
  • Anonymous Functions & Closures: Functions defined inline that can capture variables from their outer scope.

These examples illustrate these powerful concepts.

v
Run
#!/usr/local/bin/v run

fn log(msg string) {
	println(msg)
}

cnt := 2

for i in 0 .. cnt {
	log('iteration ${i}')
}

Functions Module Variables - Main (main.v)

Functions Module Variables - Main

V functions support several advanced features:

  • Multiple Return Values: A function can return more than one value (often a result and an error).
  • Blank Identifier (_): Used to discard unwanted return values.
  • Defer: Schedules a block of code to run right before the function exits, which is excellent for resource cleanup.
  • Anonymous Functions & Closures: Functions defined inline that can capture variables from their outer scope.

These examples illustrate these powerful concepts.

Additional Context from Repository docs:

This example demonstrates the concepts of main.

v
Run
// file: main.v
module main

import mymod

fn main() {
	mymod.msg := 'global variable demo'
	println(mymod.msg)
}

Mymod

Mymod

V functions support several advanced features:

  • Multiple Return Values: A function can return more than one value (often a result and an error).
  • Blank Identifier (_): Used to discard unwanted return values.
  • Defer: Schedules a block of code to run right before the function exits, which is excellent for resource cleanup.
  • Anonymous Functions & Closures: Functions defined inline that can capture variables from their outer scope.

These examples illustrate these powerful concepts.

Additional Context from Repository docs:

This example demonstrates the concepts of mymod.

v
Run
// file: mymod/mymod.v
module mymod

__global (
	msg string
)

Functions With Optional Return Types Example 1

Functions With Optional Return Types Example 1

V functions support several advanced features:

  • Multiple Return Values: A function can return more than one value (often a result and an error).
  • Blank Identifier (_): Used to discard unwanted return values.
  • Defer: Schedules a block of code to run right before the function exits, which is excellent for resource cleanup.
  • Anonymous Functions & Closures: Functions defined inline that can capture variables from their outer scope.

These examples illustrate these powerful concepts.

Additional Context from Repository docs:

This example demonstrates the concepts of functions with optional return types example 1.

v
Run
module main

fn is_teen(age int) ?string {
	if age < 0 {
		return none
	} else if age >= 13 && age <= 19 {
		return 'teenager'
	} else {
		return 'not teenager'
	}
}

fn main() {
	x := is_teen(-3) or { 'invalid age provided' }
	println(x)
}

Function With Optional Return Type Example 2

Function With Optional Return Type Example 2

V functions support several advanced features:

  • Multiple Return Values: A function can return more than one value (often a result and an error).
  • Blank Identifier (_): Used to discard unwanted return values.
  • Defer: Schedules a block of code to run right before the function exits, which is excellent for resource cleanup.
  • Anonymous Functions & Closures: Functions defined inline that can capture variables from their outer scope.

These examples illustrate these powerful concepts.

Additional Context from Repository docs:

This example demonstrates the concepts of function with optional return type example 2.

v
Run
module main

fn is_teen(age int) ?string {
	if age < 0 {
		return error('invalid age provided')
	} else if age >= 13 && age <= 19 {
		return 'teenager'
	} else {
		return 'not teenager'
	}
}

fn main() {
	x := is_teen(-3) or { err.msg }
	println(x)
}

Mod1

Mod1

V functions support several advanced features:

  • Multiple Return Values: A function can return more than one value (often a result and an error).
  • Blank Identifier (_): Used to discard unwanted return values.
  • Defer: Schedules a block of code to run right before the function exits, which is excellent for resource cleanup.
  • Anonymous Functions & Closures: Functions defined inline that can capture variables from their outer scope.

These examples illustrate these powerful concepts.

Additional Context from Repository docs:

This example demonstrates the concepts of mod1.

v
Run
// file: mod1/mod1.v
module mod1

fn greet1() string {
	return 'Hello from greet1'
}

pub fn greet2() string {
	return 'Hello from greet2'
}

pub fn greet_and_wish() string {
	wish := 'Have a nice day!'
	return greet1() + ', ' + wish
}

Public Function Demo1

Public Function Demo1

V functions support several advanced features:

  • Multiple Return Values: A function can return more than one value (often a result and an error).
  • Blank Identifier (_): Used to discard unwanted return values.
  • Defer: Schedules a block of code to run right before the function exits, which is excellent for resource cleanup.
  • Anonymous Functions & Closures: Functions defined inline that can capture variables from their outer scope.

These examples illustrate these powerful concepts.

Additional Context from Repository docs:

This example demonstrates the concepts of public function demo1.

v
Run
// file: public_function_demo1.v
import mod1

fn main() {
	g := mod1.greet1()
	println(g)
}

Public Function Demo2

Public Function Demo2

V functions support several advanced features:

  • Multiple Return Values: A function can return more than one value (often a result and an error).
  • Blank Identifier (_): Used to discard unwanted return values.
  • Defer: Schedules a block of code to run right before the function exits, which is excellent for resource cleanup.
  • Anonymous Functions & Closures: Functions defined inline that can capture variables from their outer scope.

These examples illustrate these powerful concepts.

Additional Context from Repository docs:

This example demonstrates the concepts of public function demo2.

v
Run
// file: public_function_demo2.v
import mod1

fn main() {
	g := mod1.greet2()
	println(g)
}

Public Function Demo3

Public Function Demo3

V functions support several advanced features:

  • Multiple Return Values: A function can return more than one value (often a result and an error).
  • Blank Identifier (_): Used to discard unwanted return values.
  • Defer: Schedules a block of code to run right before the function exits, which is excellent for resource cleanup.
  • Anonymous Functions & Closures: Functions defined inline that can capture variables from their outer scope.

These examples illustrate these powerful concepts.

Additional Context from Repository docs:

This example demonstrates the concepts of public function demo3.

v
Run
// file: public_function_demo3.v
import mod1

fn main() {
	g := mod1.greet_and_wish()
	println(g)
}

Function With Defer Block

Function With Defer Block

defer is useful when a function needs to clean up something before it exits, such as closing a file or releasing a resource. The deferred block runs automatically at the end of the function.

Additional Context from Repository docs:

This example demonstrates the concepts of function with defer block.

v
Run
module main

fn void_func_defer() {
	println('Hello')
	defer {
		println('Hi from defer block')
	}
	println('How are you?')

	// the defer block will be executed when the execution control reaches here
}

fn main() {
	void_func_defer()
}

Functions As Elements Of Array Or Map

Functions As Elements Of Array Or Map

Functions can be stored in arrays and maps just like other values. This allows you to choose an operation dynamically at runtime, which is helpful in flexible programs.

Additional Context from Repository docs:

This example demonstrates the concepts of functions as elements of array or map.

v
Run
module main

fn adder(i int, j int) int {
	return i + j
}

fn subtractor(i int, j int) int {
	return i - j
}

fn multiplier(i int, j int) int {
	return i * j
}

fn main() {
	i, j := 2, 5
	println('Functions as elements of an Array')
	funcs := [adder, subtractor, multiplier]

	for f in funcs {
		res := f(i, j)
		println(res)
	}
	println('Functions as elements of Map')
	d := {
		'sum':        adder
		'difference': subtractor
		'product':    multiplier
	}

	for key, val in d {
		res := val(i, j)
		println('${key} of ${i} and ${j}: ${res}')
	}
}

Function Extras

Hello

Hello

Every V program starts with main(). It is the entry point where execution begins, so it is the first function most beginners learn.

Additional Context from Repository docs:

This example demonstrates the concepts of hello.

v
Run
module main

fn main() {
	println('Welcome to the World of V!')
}

Basic Functions

Basic Functions

A basic function packages a task so you can call it later instead of repeating the same code. This example shows a function that prints a message when invoked.

Additional Context from Repository docs:

This example demonstrates the concepts of basic functions.

v
Run
fn greet(msg string) {
	println(msg)
}

fn main() {
	greet('Hello, Welcome to the world of V programming')
}

Anonymous Functions

Anonymous Functions

Anonymous functions are defined inline and are useful for short, one-off behavior. They are handy when you want a quick callback without creating a named function.

Additional Context from Repository docs:

This example demonstrates the concepts of anonymous functions.

v
Run
module main

fn main() {
	greet := fn (name string) {
		println('Hello, ${name}')
	}
	greet('Pavan')
	greet('Sahithi')
}

Functions As Input Arguments

Functions As Input Arguments

Functions are reusable blocks of logic. This lesson on Functions As Input Arguments explains functional syntax, arguments, returns, or functional capabilities in V.

Additional Context from Repository docs:

This example demonstrates the concepts of functions as input arguments.

v
Run
module main

fn greet_morning() string {
	return 'Good Morning'
}

fn greet_noon() string {
	return 'Good Afternoon'
}

fn greet_evening() string {
	return 'Good Evening'
}

fn greet(f fn () string, name string) string {
	return '${f()}, ${name}!'
}

fn main() {
	mut res := greet(greet_morning, 'Pavan')
	println(res)

	res = greet(greet_evening, 'Sahithi')
	println(res)

	res = greet(fn () string {
		return 'New year greetings to you'
	}, 'Sahithi')
	println(res)
}

Functions That Return Other Functions

Functions That Return Other Functions

Functions are reusable blocks of logic. This lesson on Functions That Return Other Functions explains functional syntax, arguments, returns, or functional capabilities in V.

Additional Context from Repository docs:

This example demonstrates the concepts of functions that return other functions.

v
Run
module main

enum Operation {
	add
	sub
	mul
}

fn adder(i int, j int) int {
	return i + j
}

fn subtractor(i int, j int) int {
	return i - j
}

fn multiplier(i int, j int) int {
	return i * j
}

fn fetch(op Operation) fn (int, int) int {
	return match op {
		.add {
			adder
		}
		.sub {
			subtractor
		}
		.mul {
			multiplier
		}
	}
}

fn main() {
	i, j := 2, 5
	mut f := fetch(.add) // return adder function
	mut res := f(i, j) // calls adder(2, 5)
	println('sum of ${i} and ${j}: ${res}')

	f = fetch(.sub) // returns subtractor function
	res = f(i, j) // calls subtractor(2, 5)
	println('difference of ${i} and ${j}: ${res}')

	f = fetch(.mul) // returns multipler function
	res = f(i, j) // calls multiplier(2, 5)
	println('product of ${i} and ${j}: ${res}')
}

Chapter 7 Structs (Custom 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:

Struct Basics & Fields


Structs are user-defined data structures that allow you to group related fields together. This chapter explains how to define structs, set default values, make fields required, attach methods to structs, and embed structs inside other structures.

Struct Basics & Fields

Defining Struct

Defining Struct

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of defining struct.

v
Run
struct Note {
	id      int
	message string
}

fn main() {
}

Initialize Struct Example 1

Initialize Struct Example 1

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of initialize struct example 1.

v
Run
struct Note {
	id      int
	message string
}

fn main() {
	n := Note{1, 'a simple struct demo'}

	println(n)
}

Initialize Struct Example 2

Initialize Struct Example 2

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of initialize struct example 2.

v
Run
struct Note {
	id      int
	message string
}

fn main() {
	n := Note{
		message: 'a simple struct demo'
		id:      1
	}

	println(typeof(n).name)
	// Note
}

Access Struct Fields

Access Struct Fields

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of access struct fields.

v
Run
struct Note {
	id      int
	message string
}

fn main() {
	n := Note{1, 'a simple struct demo'}
	println(n.message)
}

Heap Structs

Heap Structs

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of heap structs.

v
Run
struct Note {
	id      int
	message string
}

fn main() {
	n1 := &Note{1, 'this note will be allocated on heap'}
	println(typeof(n1).name) // &Note
}

Updating Immutable Struct Variable Throws Error

Updating Immutable Struct Variable Throws Error

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of updating immutable struct variable throws error.

v
Run
module main

struct Note {
	id int
mut:
	message string
}

fn main() {
	n := Note{1, 'a simple struct demo'}
	println(n)

	n.message = 'a simple struct updated' // throws error
}

Updating Mutable Fields Of Struct

Updating Mutable Fields Of Struct

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of updating mutable fields of struct.

v
Run
module main

struct Note {
	id int
mut:
	message string
}

fn main() {
	mut n := Note{1, 'a simple struct demo'}
	println('before update')
	println(n)

	n.message = 'a simple struct updated'
	println('after update')
	println(n)
}

Updating Immutable Fields Throws Error

Updating Immutable Fields Throws Error

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of updating immutable fields throws error.

v
Run
module main

struct Note {
	id int
mut:
	message string
}

fn main() {
	mut j := Note{1, 'a simple struct demo'}
	j.id = 2 // throws error
}

Updating Struct With Unspecified Fields Are Zeroed

Updating Struct With Unspecified Fields Are Zeroed

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of updating struct with unspecified fields are zeroed.

v
Run
module main

struct Note {
	id int
mut:
	message string
}

fn main() {
	// declare
	mut n := Note{}

	// populate
	n = Note{
		id:      1
		message: 'updating struct fields demo'
	}
	println(n)

	// unspecified fields zeroed by default
	// id being type of int, will become 0 here
	println('unspecified id zeroed during short struct type initialization')
	n = Note{
		message: 'updating struct fields demo 2'
	}
	println(n)
}

Struct With Multiple Fields

Struct With Multiple Fields

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of struct with multiple fields.

v
Run
struct Note {
	id int
mut:
	message string
	status  bool
}

fn main() {
}

Grouping Struct Fields Based On Access Modifiers

Grouping Struct Fields Based On Access Modifiers

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of grouping struct fields based on access modifiers.

v
Run
pub struct Note {
pub:
	id int
pub mut:
	message string
	status  bool
}

fn main() {
}

Required Fields Example 01

Required Fields Example 01

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of required fields example 01.

v
Run
pub struct Note {
pub:
	id int
pub mut:
	message string @[required]
	status  bool
}

fn main() {
	_ := Note{
		id:     1
		status: false
	}
}
// throws error

Required Fields Example 02

Required Fields Example 02

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of required fields example 02.

v
Run
module main

pub struct Note {
pub:
	id int
pub mut:
	message string @[required]
	status  bool
}

fn main() {
	n := Note{
		id:      1
		message: 'a simple struct demo'
		status:  false
	}
	println(n)
}

Struct Fields With Default Values

Struct Fields With Default Values

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of struct fields with default values.

v
Run
import time

pub struct Note {
pub:
	id      int
	created time.Time = time.now()
pub mut:
	message string @[required]
	status  bool
	due     time.Time = time.now().add_days(1)
}

fn main() {
	n := Note{
		id:      1
		message: 'order groceries'
	}
	println(n)
}

Methods For Struct

Methods For Struct

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of methods for struct.

v
Run
module main

import time

pub struct Note {
pub:
	id      int
	created time.Time = time.now()
pub mut:
	message string @[required]
	status  bool
	due     time.Time = time.now().add_days(1)
}

// is_empty_message is a method that belongs to Note
pub fn (n Note) is_empty_message() bool {
	return n.message.len < 1
}

fn main() {
	mut n := Note{
		id:      1
		message: ''
	}

	if n.is_empty_message() {
		println('message is empty')
	} else {
		println('message not empty')
	}
}

Adding Struct As Struct Field

Adding Struct As Struct Field

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of adding struct as struct field.

v
Run
import time

// NoteTimeInfo is a struct to store time info of Note
pub struct NoteTimeInfo {
pub:
	created time.Time = time.now()
pub mut:
	due time.Time = time.now().add_days(1)
}

// Note is a struct with struct NoteTimeInfo as a field, along with other fields
pub struct Note {
	NoteTimeInfo // Struct as another struct field
pub:
	id int
pub mut:
	message string @[required]
	status  bool
}

fn main() {
	n := Note{
		id:      1
		message: 'adding struct as struct field demo'
	}
	println('Due date: ${n.due}')
	println(n)
}

Updating Fields Of Type Struct

Updating Fields Of Type Struct

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of updating fields of type struct.

v
Run
module main

import time

// NoteTimeInfo is a struct to store time info of Note
pub struct NoteTimeInfo {
pub:
	created time.Time = time.now()
pub mut:
	due time.Time = time.now().add_days(1)
}

// Note is a struct with struct NoteTimeInfo as a field, along with other fields
pub struct Note {
	NoteTimeInfo
pub:
	id int
pub mut:
	message string @[required]
	status  bool
}

fn main() {
	mut n := Note{
		id:      1
		message: 'adding struct as struct field demo'
	}

	println('Due date: ${n.due}')
	// approach 1: implicit access of struct fields of fields of type struct
	n.due = n.due.add_days(2)
	println('Due date after update: ${n.due}')

	// approach 2: explicitly specifying the field of type struct and its fields
	n.NoteTimeInfo.due = n.NoteTimeInfo.due.add_days(2)
	println('Due date updated second time: ${n.due}')
	println(n)
}

Struct As Trailing Literal Arguments To Function

Struct As Trailing Literal Arguments To Function

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of struct as trailing literal arguments to function.

v
Run
module main

import time

// NoteTimeInfo is a struct to store time info of Note
pub struct NoteTimeInfo {
pub:
	created time.Time = time.now()
pub mut:
	due time.Time = time.now().add_days(1)
}

// Note is a struct with embedding struct NoteTimeInfo along with other fields
pub struct Note {
	NoteTimeInfo
pub:
	id int
pub mut:
	message string @[required]
	status  bool
}

fn new_grocery_note(n Note) &Note {
	return &Note{
		id:      n.id
		message: 'Buy Groceries: ' + n.message
	}
}

fn extend_due_by_a_day(n Note) &Note {
	return &Note{
		NoteTimeInfo: NoteTimeInfo{
			due: n.due.add_days(1)
		}
		id:           n.id
		message:      n.message
	}
}

fn main() {
	g := new_grocery_note(Note{ id: 1, message: 'Milk' })
	println('${g.message} is due by ${g.due}')
	n := extend_due_by_a_day(g)
	println('After extending due date by a day')
	println('${n.message} is due by ${n.due}')
}

Chapter 8 Error Handling

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:

Option & Result Types


V has no exceptions. Instead, it handles errors using Option and Result types, which are checked at compile time. This chapter teaches you how to write robust, error-free programs using V's clean error handling syntax.

Option & Result Types

Error Handling

In many programming languages, errors are handled using exceptions (with try, catch, and throw blocks). Exception blocks can make code hard to read and trace because control flow can jump unpredictably.

V takes a different approach. V does not have exceptions. Instead, V handles errors explicitly using two main concepts:

  • Option Types (?T): Used when a value might simply be missing (like searching for an item that isn't in a list).
  • Result Types (!T): Used when an operation might actually fail with a specific error (like division by zero or a database timeout).

By forcing you to handle these outcomes explicitly, V makes your code safer and easier to debug.


v
Run
module main

// ==========================================
// Define Custom Error Types
// ==========================================

// CustomError embeds the builtin Error struct to implement the IError interface.
struct CustomError {
	Error // Required: provides default implementations of msg() and code()
	message string
	code    int
}

// Overwrite the msg() method for CustomError
fn (err CustomError) msg() string {
	return err.message
}

// Overwrite the code() method for CustomError
fn (err CustomError) code() int {
	return err.code
}

// DatabaseError represents another custom error type.
struct DatabaseError {
	Error
	query string
}

fn (err DatabaseError) msg() string {
	return 'Database error executing query: "${err.query}"'
}

// ==========================================
// 1. Option Types (?T)
// Options represent either a value of type T or nothing (none).
// ==========================================

// find_item returns a string if found, or none if not.
fn find_item(id int) ?string {
	if id == 42 {
		return 'V programming book'
	}
	return none // return absence of value
}

// find_item_wrapper demonstrates Option propagation with the `?` suffix.
fn find_item_wrapper(id int) ?string {
	// If find_item returns none, the execution stops here and propagates none up.
	item := find_item(id)?
	return 'Found: ' + item
}

// ==========================================
// 2. Result Types (!T)
// Results represent either a value of type T or an IError.
// ==========================================

// divide performs float division but returns an error for division by zero.
fn divide(a f64, b f64) !f64 {
	if b == 0.0 {
		return error('division by zero') // Return a standard error
	}
	return a / b
}

// fetch_data returns a string or a CustomError.
fn fetch_data(success bool) !string {
	if !success {
		return CustomError{
			message: 'Connection timed out'
			code: 504
		}
	}
	return 'Raw database records'
}

// query_db returns a string or a DatabaseError.
fn query_db(query string, success bool) !string {
	if !success {
		return DatabaseError{
			query: query
		}
	}
	return 'Query success'
}

// calculate_and_format demonstrates Result propagation using the `!` operator.
fn calculate_and_format(a f64, b f64) !string {
	// The `!` suffix propagates the error to the caller if divide fails.
	res := divide(a, b)!
	return 'Result is ${res:.2f}'
}

// ==========================================
// 3. Unrecoverable Errors (Panics)
// ==========================================
fn force_panic() {
	println('Simulating a critical failure...')
	panic('Fatal error: Out of memory or system crash.')
}

fn main() {
	println('=== 1. Option Types (?T) ===')

	// Option Handling: Option unwrapping using `or` block
	item_1 := find_item(42) or { 'Default Item' }
	println('Item 1 (with 42): ${item_1}')

	item_2 := find_item(99) or { 'Default Item' }
	println('Item 2 (with 99): ${item_2}')

	// Option Handling: Option unwrapping with variable binding using `if-let`
	if item := find_item(42) {
		println('If-let match: Found "${item}"')
	} else {
		println('If-let match: Item not found')
	}

	if item := find_item(99) {
		println('If-let match: Found "${item}"')
	} else {
		println('If-let match: Item not found (none)')
	}

	// Option Propagation Check
	wrapped_item := find_item_wrapper(99) or { 'None propagated successfully' }
	println('Propagation check: ${wrapped_item}\n')


	println('=== 2. Result Types (!T) ===')

	// Result Handling: Standard error message extraction via the `err` variable inside `or` block
	calc_success := calculate_and_format(10.0, 2.0) or { 'Error: ${err}' }
	println('Calc success: ${calc_success}')

	calc_fail := calculate_and_format(10.0, 0.0) or { 'Error: ${err}' }
	println('Calc failure: ${calc_fail}')


	println('\n=== 3. Custom Error Matching & Type Casting ===')

	// We can inspect the error type dynamically using the `is` check inside the `or` block.
	// Since fetch_data(false) returns a Result type (!string), the `or` block must either:
	// 1. Terminate control flow (e.g. using return, panic, exit)
	// 2. Evaluate to a fallback string value.
	// We use `''` (empty string) here as the fallback value to satisfy this type requirement.
	fetch_data(false) or {
		if err is CustomError {
			// Inside this block, `err` is smart-cast to CustomError automatically,
			// allowing direct access to custom fields like `code`.
			println('Caught CustomError! Message: "${err.msg()}", Code: ${err.code}')
		} else {
			println('Caught generic error: ${err.msg()}')
		}
		'' // Fallback empty string returned to satisfy the !string return type of the or block
	}

	// Similarly, query_db returns !string, so its or block must also evaluate to a string.
	query_db('SELECT * FROM users', false) or {
		if err is DatabaseError {
			// Smart-cast to DatabaseError, accessing the `query` field
			println('Caught DatabaseError!')
			println('Query attempted: "${err.query}"')
			println('Error message:   "${err.msg()}"')
		} else {
			println('Caught generic error: ${err.msg()}')
		}
		'' // Fallback empty string returned to satisfy the !string return type of the or block
	}


	println('\n=== 4. Panic (Unrecoverable Error) ===')
	// We wrap panic execution or run it last since it terminates the process.
	// You can uncomment the line below to test panic termination:
	// force_panic()
	println('To run a panic, uncomment force_panic() in main.')
}

Chapter 9 Organizing Code with Modules

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:

Modules & Project Structure

Installing External Packages


Modules help organize larger codebases. In this chapter, you will learn how to create modules, import them, manage member visibility using pub, and understand module initialization lifecycle.

Modules & Project Structure

Creating a Simple V Project - Main (modulebasics.v)

Creating a Simple V Project

Think of a module as a small toolbox. The main module is the entry point of your program, while other modules can hold reusable functions and types.

This first example is intentionally simple: it shows the structure of a single-file V program before we introduce imports and shared modules.

v
Run
module main

fn main() {
	println('Welcome to the V module demo!')
}

Creating a Module - Helper (file1.v)

A Reusable Helper Module

A module can hold functions that other parts of your program can reuse. In this example, the helper module mod1 exposes a public function called greet.

v
Run
module mod1

pub fn greet(name string) string {
	return 'Hello, ${name}!'
}

Creating a Module - Main (modulebasics.v)

Module Main Entry

This file acts as the application entry point. It imports the helper module and calls one of its public functions.

v
Run
module main

import mod1

fn main() {
	println(mod1.greet('Ada'))
}

Importing a Module - Helper (file1.v)

Imported Module Helper

Importing a module gives your program access to its public members. The module name becomes the namespace you use when calling those functions.

v
Run
module mod1

pub fn greet(name string) string {
	return 'Hello, ${name}!'
}

Importing a Module - Main (modulebasics.v)

Imported Module Main

The main program can now use the imported module without copying its code into the entry file.

v
Run
module main

import mod1

fn main() {
	message := mod1.greet('Lina')
	println(message)
}

Accessing Module Members - Helper (file1.v)

Public vs Private Members

Not everything in a module should be accessible from outside. In V, pub makes a function available to other modules, while private functions stay inside the module.

v
Run
module mod1

pub fn greet(name string) string {
	return 'Hello, ${name}!'
}

fn internal_note() string {
	return 'This helper stays inside the module.'
}

Accessing Module Members - Main (modulebasics.v)

Member Visibility Main

From the main program, you can call the public function, but private helpers remain hidden.

v
Run
module main

import mod1

fn main() {
	println(mod1.greet('Noor'))
	// mod1.internal_note() is not allowed from here
}

Multiple Files (After Refactoring) - Helper 1 (file1.v)

Multiple Files (After Refactoring) - Helper 1

A single module can be split across several files. This makes it easier to keep related helpers organized without changing how the module is imported.

v
Run
module mod1

pub fn hello() {
	println('Hello from mod1!')
}

Multiple Files (After Refactoring) - Helper 2 (file2.v)

Multiple Files (After Refactoring) - Helper 2

The second file in the same module can hold additional helper functions. The module still behaves as one logical unit when imported.

v
Run
module mod1

fn hello2() {
	println('Hello 2 from mod1!')
}

Multiple Files (After Refactoring) - Main (modulebasics.v)

Multiple Files (After Refactoring) - Main Entry

Modules help modularize V projects, managing imports and symbol visibility. This lesson on Modulebasics demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

Additional Context from Repository docs:

This example demonstrates the concepts of modulebasics.

v
Run
module main

import mod1

fn main() {
	mod1.hello()
	println('Hello World!')
}

Multiple Files (Before Refactoring) - Helper 1 (file1.v)

Multiple Files (Before Refactoring) - Helper 1

Modules help modularize V projects, managing imports and symbol visibility. This lesson on File1 demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

Additional Context from Repository docs:

This example demonstrates the concepts of file1.

v
Run
module mod1

pub fn hello() {
	println('Hello from mod1!')
}

Multiple Files (Before Refactoring) - Helper 2 (file2.v)

Multiple Files (Before Refactoring) - Helper 2

Modules help modularize V projects, managing imports and symbol visibility. This lesson on File2 demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

Additional Context from Repository docs:

This example demonstrates the concepts of file2.

v
Run
fn hello2() {
	println('Hello 2 from mod1!')
}

fn main() {
}

Multiple Files (Before Refactoring) - Main (modulebasics.v)

Multiple Files (Before Refactoring) - Main Entry

Modules help modularize V projects, managing imports and symbol visibility. This lesson on Modulebasics demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

Additional Context from Repository docs:

This example demonstrates the concepts of modulebasics.

v
Run
module main

import mod1

fn main() {
	mod1.hello()
	println('Hello World!')
}

Member Scope (After Refactoring) - Helper 1 (file1.v)

Member Scope (After Refactoring) - Helper 1

A function marked pub can be called from outside the module, while a private helper can still be used by other functions inside the same module.

v
Run
module mod1

pub fn hello() {
	println('Hello from mod1!')
	// hello2 is not a public but accessible within mod1
	hello2()
}

Member Scope (After Refactoring) - Helper 2 (file2.v)

Member Scope (After Refactoring) - Helper 2

Modules help modularize V projects, managing imports and symbol visibility. This lesson on File2 demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

Additional Context from Repository docs:

This example demonstrates the concepts of file2.

v
Run
module mod1

fn hello2() {
	println('Hello 2 from mod1!')
}

Member Scope (After Refactoring) - Main (modulebasics.v)

Member Scope (After Refactoring) - Main Entry

Modules help modularize V projects, managing imports and symbol visibility. This lesson on Modulebasics demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

Additional Context from Repository docs:

This example demonstrates the concepts of modulebasics.

v
Run
module main

import mod1

fn main() {
	mod1.hello()
}

Member Scope (Before Refactoring) - Helper 1 (file1.v)

Member Scope (Before Refactoring) - Helper 1

Modules help modularize V projects, managing imports and symbol visibility. This lesson on File1 demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

Additional Context from Repository docs:

This example demonstrates the concepts of file1.

v
Run
module mod1

pub fn hello() {
	println('Hello from mod1!')
}

Member Scope (Before Refactoring) - Helper 2 (file2.v)

Member Scope (Before Refactoring) - Helper 2

Modules help modularize V projects, managing imports and symbol visibility. This lesson on File2 demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

Additional Context from Repository docs:

This example demonstrates the concepts of file2.

v
Run
module mod1

fn hello2() {
	println('Hello 2 from mod1!')
}

Member Scope (Before Refactoring) - Main (modulebasics.v)

Member Scope (Before Refactoring) - Main Entry

Modules help modularize V projects, managing imports and symbol visibility. This lesson on Modulebasics demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

Additional Context from Repository docs:

This example demonstrates the concepts of modulebasics.

v
Run
module main

import mod1

fn main() {
	mod1.hello()
	mod1.hello2()
}

Cyclic Imports - Module 1 Helper (file1.v)

Cyclic Imports - Module 1

This example shows a circular dependency between two modules. In practice, you should avoid this pattern because it makes the import graph harder to reason about.

v
Run
module m1

import m2

pub const greet_from_m1 = 'Greetings from m1'

pub fn hello() {
	println(m2.greet_from_m2)
}

Cyclic Imports - Module 2 Helper (file1.v)

Cyclic Imports - Module 2

Modules help modularize V projects, managing imports and symbol visibility. This lesson on File1 demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

Additional Context from Repository docs:

This example demonstrates the concepts of file1.

v
Run
module m2

import m1

pub const greet_from_m2 = 'Greetings from m2'

pub fn hello() {
	println(m1.greet_from_m1)
}

Cyclic Imports - Main (modulebasics.v)

Cyclic Imports - Main Entry

Modules help modularize V projects, managing imports and symbol visibility. This lesson on Modulebasics demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

Additional Context from Repository docs:

This example demonstrates the concepts of modulebasics.

v
Run
module main

import m1
import m2

fn main() {
	m1.hello()
	m2.hello()
}

Module Init Function - Helper (file1.v)

Module Init Function Helper

The special init() function runs automatically when the module is loaded, which is useful for one-time setup work.

v
Run
module mod1

pub fn hello() {
	println('Hello from mod1!')
}

fn init() {
	println('Initializing mod1')
}

Module Init Function - Main (modulebasics.v)

Module Init Function

Modules help modularize V projects, managing imports and symbol visibility. This lesson on Modulebasics demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

Additional Context from Repository docs:

This example demonstrates the concepts of modulebasics.

v
Run
module main

import mod1

fn main() {
	mod1.hello()
}

Accessing Module Constants - Helper (file1.v)

Accessing Module Constants Helper

Constants are shared values that belong to a module. They are great for configuration strings or fixed messages that multiple files can use.

v
Run
module mod1

pub const greet_msg = 'Greeting from mod1!'

Accessing Module Constants - Main (modulebasics.v)

Accessing Module Constants

Modules help modularize V projects, managing imports and symbol visibility. This lesson on Modulebasics demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

Additional Context from Repository docs:

This example demonstrates the concepts of modulebasics.

v
Run
module main

import mod1

fn main() {
	println(mod1.greet_msg)
}

Accessing Module Structs - Helper (file1.v)

Accessing Module Structs Helper

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of file1.

v
Run
module mod1

import time

// NoteTimeInfo is a struct to store time info of Note
pub struct NoteTimeInfo {
pub:
	created time.Time = time.now()
pub mut:
	due time.Time = time.now().add_days(1)
}

// Note is a struct with embedding struct NoteTimeInfo along with other fields
pub struct Note {
	NoteTimeInfo // Embedded Struct
pub:
	id int
pub mut:
	message string @[required]
	status  bool
}

Accessing Module Structs - Main (modulebasics.v)

Accessing Module Structs

A struct is a user-defined custom type that groups related variables (called fields) together. Structs are fundamental to V's object-oriented programming model. By default, struct fields are private and immutable. V provides access modifiers like mut:, pub:, and pub mut: to control field access and mutability.

These examples demonstrate defining structs, updating fields, required fields, default values, and struct methods.

Additional Context from Repository docs:

This example demonstrates the concepts of modulebasics.

v
Run
module main

import mod1

fn main() {
	n := mod1.Note{
		id:      1
		message: 'Accessing structs of module demo'
	}
	println('Accessing struct field value Note id: ${n.id}')
	println('Accessing embedded struct field value NoteTimeInfo: ${n.NoteTimeInfo}')
}

Installing External Packages

V has a built-in package manager called vpm (V Package Manager) that allows you to easily install, update, and manage third-party modules. External packages are hosted on the official V registry at vpm.vlang.io.

How to Install Packages with vpm

To install a package, use the v install command followed by the package identifier (usually in the format author.package_name):

bash
v install xiusin.vredis

This downloads the package and installs it into the V modules directory (typically located at ~/.vmodules/ on Linux/macOS or C:\Users\Username\.vmodules\ on Windows).

Common vpm Commands

  • Install a package:
bash
  v install author.package_name
  • Install from a Git repository directly:
bash
  v install https://github.com/author/package_name
  • Update an installed package:
bash
  v update author.package_name
  • Remove/uninstall a package:
bash
  v remove author.package_name
  • Search for packages:
bash
  v search query

Importing and Using External Packages

Once a package is installed via vpm, you can import it in your V code just like a standard library module:

v
Run
import xiusin.vredis

fn main() {
	// Code utilizing the external redis package
}

Redis Console Demo

This example demonstrates how to use the external xiusin.vredis client package in a console application and demonstrates key namespacing with the custom NamespacedRedis helper. It covers:

  • Establishing a connection and handling errors gracefully.
  • Basic String operations (set, get, incr, expire, ttl, del).
  • List operations (rpush, llen, lrange, lpop).
  • Hash operations (hset, hget, hgetall).
  • Set operations (sadd, sismember, smembers).
  • Namespaced Redis helper operations using NamespacedRedis.
  • Cleaning up created keys on application exit.
v
Run
module main

import xiusin.vredis

fn main() {
	println('==================================================')
	println('       V + Redis Console API Learning Demo        ')
	println('==================================================')

	println('Connecting to local Redis at 127.0.0.1:6379...')
	mut r := vredis.new_client(host: '127.0.0.1', port: 6379) or {
		eprintln('\n[ERROR] Failed to connect to Redis server: ${err}')
		eprintln('Please make sure Redis is running locally on port 6379.')
		return
	}
	defer {
		r.close() or {}
		println('\n==================================================')
		println('Demo completed. Redis connection closed.')
		println('==================================================')
	}

	println('Connected successfully!\n')

	// Clean up any old test keys first
	r.del('demo:string') or {}
	r.del('demo:counter') or {}
	r.del('demo:list') or {}
	r.del('demo:hash') or {}
	r.del('demo:set') or {}

	// --- 1. String Operations ---
	println('--- 1. String Operations ---')
	println('Setting "demo:string" to "Hello V + Redis!"...')
	r.set('demo:string', 'Hello V + Redis!') or { panic(err) }

	val := r.get('demo:string') or { panic(err) }
	println('GET "demo:string" -> "${val}"')

	// Increment demo
	r.incr('demo:counter') or { panic(err) }
	r.incr('demo:counter') or { panic(err) }
	counter_val := r.get('demo:counter') or { panic(err) }
	println('Counter INCR twice -> "${counter_val}"')

	// TTL Demo
	println('Setting expiry of 5 seconds on "demo:string"...')
	r.expire('demo:string', 5) or { panic(err) }
	ttl_val := r.ttl('demo:string') or { panic(err) }
	println('TTL remaining: ${ttl_val} seconds\n')

	// --- 2. List Operations ---
	println('--- 2. List Operations ---')
	println('Pushing items to "demo:list" (item_a, item_b, item_c)...')
	r.rpush('demo:list', 'item_a') or { panic(err) }
	r.rpush('demo:list', 'item_b') or { panic(err) }
	r.rpush('demo:list', 'item_c') or { panic(err) }

	list_len := r.llen('demo:list') or { panic(err) }
	println('List length: ${list_len}')

	list_items := r.lrange('demo:list', 0, -1) or { panic(err) }
	println('List elements: ${list_items}')

	popped := r.lpop('demo:list') or { panic(err) }
	println('Popped from left (LPOP): "${popped}"')

	list_items_after := r.lrange('demo:list', 0, -1) or { panic(err) }
	println('List elements after LPOP: ${list_items_after}\n')

	// --- 3. Hash Operations ---
	println('--- 3. Hash Operations ---')
	println('Setting fields in "demo:hash"...')
	r.hset('demo:hash', 'name', 'V Programming Language') or { panic(err) }
	r.hset('demo:hash', 'year', '2019') or { panic(err) }
	r.hset('demo:hash', 'creator', 'Alex Medvednikov') or { panic(err) }

	name_field := r.hget('demo:hash', 'name') or { panic(err) }
	println('HGET "demo:hash" "name" -> "${name_field}"')

	hash_all := r.hgetall('demo:hash') or { panic(err) }
	println('HGETALL "demo:hash" fields & values:')
	for k, v in hash_all {
		println('  - ${k}: ${v}')
	}
	println('')

	// --- 4. Set Operations ---
	println('--- 4. Set Operations ---')
	println('Adding members to "demo:set"...')
	r.sadd('demo:set', 'apple') or { panic(err) }
	r.sadd('demo:set', 'banana') or { panic(err) }
	r.sadd('demo:set', 'apple') or { panic(err) } // Duplicate (should be ignored)

	is_banana := r.sismember('demo:set', 'banana') or { panic(err) }
	is_cherry := r.sismember('demo:set', 'cherry') or { panic(err) }
	println('SISMEMBER "demo:set" "banana": ${is_banana}')
	println('SISMEMBER "demo:set" "cherry": ${is_cherry}')

	set_members := r.smembers('demo:set') or { panic(err) }
	println('SMEMBERS "demo:set": ${set_members}\n')

	// --- 5. Namespaced Helper Demo ---
	println('--- 5. Namespaced Helper Demo ---')
	println('Creating a namespaced helper with namespace "app_v1"...')
	mut nr := new_namespaced_redis(r, 'app_v1')

	println('Setting namespaced key "user_token" (resolved key will be "app_v1:user_token")...')
	nr.set('user_token', 'token_abc123') or { panic(err) }

	token := nr.get('user_token') or { panic(err) }
	println('GET "user_token" via helper -> "${token}"')

	// Verify the actual key in Redis (without namespace helper) has the prefix
	actual_key := 'app_v1:user_token'
	actual_val := r.get(actual_key) or { panic(err) }
	println('GET raw "${actual_key}" directly from client -> "${actual_val}"')

	// Cleanup namespaced keys
	println('Cleaning up namespaced keys...')
	nr.del('user_token') or {}

	// Clean up test keys
	println('\nCleaning up created keys...')
	r.del('demo:string') or {}
	r.del('demo:counter') or {}
	r.del('demo:list') or {}
	r.del('demo:hash') or {}
	r.del('demo:set') or {}
	println('Cleanup done.')
}

Redis Console Demo - Helper (redis_helper.v)

This helper provides a namespaced wrapper struct NamespacedRedis that automatically prefixes all Redis keys with a given namespace (e.g. namespace:key). This is a great pattern for keeping keys organized and avoiding collisions between multiple apps/environments.

v
Run
module main

import xiusin.vredis

// NamespacedRedis wraps a standard vredis.Redis client and prefixes all keys with a namespace.
// This simplifies multi-tenant or multi-app key separation.
struct NamespacedRedis {
mut:
	client &vredis.Redis
pub:
	namespace string
}

// new_namespaced_redis creates a new NamespacedRedis helper wrapper.
fn new_namespaced_redis(client &vredis.Redis, namespace string) NamespacedRedis {
	return NamespacedRedis{
		client: client
		namespace: namespace
	}
}

// key constructs the final namespaced key.
// E.g. key('mykey') -> 'app1:mykey'
fn (nr NamespacedRedis) key(name string) string {
	if nr.namespace == '' {
		return name
	}
	return '${nr.namespace}:${name}'
}

// close closes the connection to the Redis server.
fn (mut nr NamespacedRedis) close() ! {
	nr.client.close()!
}

// --- String Operations ---

// set sets a key to a string value.
fn (mut nr NamespacedRedis) set(key string, val string) ! {
	nr.client.set(nr.key(key), val)!
}

// get retrieves a string value by key.
fn (mut nr NamespacedRedis) get(key string) !string {
	return nr.client.get(nr.key(key))!
}

// incr increments a numeric key.
fn (mut nr NamespacedRedis) incr(key string) ! {
	nr.client.incr(nr.key(key))!
}

// expire sets an expiration time (TTL) in seconds on a key.
fn (mut nr NamespacedRedis) expire(key string, seconds int) ! {
	nr.client.expire(nr.key(key), seconds)!
}

// ttl returns the remaining Time-To-Live of a key.
fn (mut nr NamespacedRedis) ttl(key string) !int {
	return nr.client.ttl(nr.key(key))!
}

// del deletes a key.
fn (mut nr NamespacedRedis) del(key string) ! {
	nr.client.del(nr.key(key))!
}

// --- List Operations ---

// rpush appends a value to a list.
fn (mut nr NamespacedRedis) rpush(key string, val string) ! {
	nr.client.rpush(nr.key(key), val)!
}

// lrange retrieves a range of elements from a list.
fn (mut nr NamespacedRedis) lrange(key string, start int, stop int) ![]string {
	return nr.client.lrange(nr.key(key), start, stop)!
}

// lpop removes and returns the first element of a list.
fn (mut nr NamespacedRedis) lpop(key string) !string {
	return nr.client.lpop(nr.key(key))!
}

// llen returns the length of a list.
fn (mut nr NamespacedRedis) llen(key string) !int {
	return nr.client.llen(nr.key(key))!
}

// --- Hash Operations ---

// hset sets a field in a hash to a value.
fn (mut nr NamespacedRedis) hset(key string, field string, val string) ! {
	nr.client.hset(nr.key(key), field, val)!
}

// hget retrieves a field's value from a hash.
fn (mut nr NamespacedRedis) hget(key string, field string) !string {
	return nr.client.hget(nr.key(key), field)!
}

// hgetall retrieves all fields and values of a hash.
fn (mut nr NamespacedRedis) hgetall(key string) !map[string]string {
	return nr.client.hgetall(nr.key(key))!
}

// --- Set Operations ---

// sadd adds a member to a set.
fn (mut nr NamespacedRedis) sadd(key string, member string) ! {
	nr.client.sadd(nr.key(key), member)!
}

// sismember checks if a member belongs to a set.
fn (mut nr NamespacedRedis) sismember(key string, member string) !bool {
	return nr.client.sismember(nr.key(key), member)!
}

// smembers returns all members of a set.
fn (mut nr NamespacedRedis) smembers(key string) ![]string {
	return nr.client.smembers(nr.key(key))!
}

Redis Namespaced Demo - Helper (redis_helper.v)

Redis Namespaced Helper

Modules help modularize V projects, managing imports and symbol visibility. This lesson on Redis Helper demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

v
Run
module main

import xiusin.vredis

// NamespacedRedis wraps a standard vredis.Redis client and prefixes all keys with a namespace.
struct NamespacedRedis {
mut:
	client &vredis.Redis
pub:
	namespace string
}

// new_namespaced_redis creates a new NamespacedRedis helper wrapper.
fn new_namespaced_redis(client &vredis.Redis, namespace string) NamespacedRedis {
	return NamespacedRedis{
		client: client
		namespace: namespace
	}
}

// key constructs the final namespaced key.
fn (nr NamespacedRedis) key(name string) string {
	if nr.namespace == '' {
		return name
	}
	return '${nr.namespace}:${name}'
}

// close closes the connection to the Redis server.
fn (mut nr NamespacedRedis) close() ! {
	nr.client.close()!
}

// --- String Operations ---

// set sets a key to a string value.
fn (mut nr NamespacedRedis) set(key string, val string) ! {
	nr.client.set(nr.key(key), val)!
}

// get retrieves a string value by key.
fn (mut nr NamespacedRedis) get(key string) !string {
	return nr.client.get(nr.key(key))!
}

// incr increments a numeric key.
fn (mut nr NamespacedRedis) incr(key string) ! {
	nr.client.incr(nr.key(key))!
}

// expire sets an expiration time (TTL) in seconds on a key.
fn (mut nr NamespacedRedis) expire(key string, seconds int) ! {
	nr.client.expire(nr.key(key), seconds)!
}

// ttl returns the remaining Time-To-Live of a key.
fn (mut nr NamespacedRedis) ttl(key string) !int {
	return nr.client.ttl(nr.key(key))!
}

// del deletes a key.
fn (mut nr NamespacedRedis) del(key string) ! {
	nr.client.del(nr.key(key))!
}

// --- List Operations ---

// rpush appends a value to a list.
fn (mut nr NamespacedRedis) rpush(key string, val string) ! {
	nr.client.rpush(nr.key(key), val)!
}

// lrange retrieves a range of elements from a list.
fn (mut nr NamespacedRedis) lrange(key string, start int, stop int) ![]string {
	return nr.client.lrange(nr.key(key), start, stop)!
}

// lpop removes and returns the first element of a list.
fn (mut nr NamespacedRedis) lpop(key string) !string {
	return nr.client.lpop(nr.key(key))!
}

// llen returns the length of a list.
fn (mut nr NamespacedRedis) llen(key string) !int {
	return nr.client.llen(nr.key(key))!
}

// --- Hash Operations ---

// hset sets a field in a hash to a value.
fn (mut nr NamespacedRedis) hset(key string, field string, val string) ! {
	nr.client.hset(nr.key(key), field, val)!
}

// hget retrieves a field's value from a hash.
fn (mut nr NamespacedRedis) hget(key string, field string) !string {
	return nr.client.hget(nr.key(key), field)!
}

// hgetall retrieves all fields and values of a hash.
fn (mut nr NamespacedRedis) hgetall(key string) !map[string]string {
	return nr.client.hgetall(nr.key(key))!
}

// --- Set Operations ---

// sadd adds a member to a set.
fn (mut nr NamespacedRedis) sadd(key string, member string) ! {
	nr.client.sadd(nr.key(key), member)!
}

// sismember checks if a member belongs to a set.
fn (mut nr NamespacedRedis) sismember(key string, member string) !bool {
	return nr.client.sismember(nr.key(key), member)!
}

// smembers returns all members of a set.
fn (mut nr NamespacedRedis) smembers(key string) ![]string {
	return nr.client.smembers(nr.key(key))!
}

Redis Namespaced Demo

This example provides an easy, dedicated demo showing how to use the NamespacedRedis helper wrapper to manage multiple independent namespaces (like cache and session) over a single underlying Redis connection without key collisions.

v
Run
module main

import xiusin.vredis

fn main() {
	println('==================================================')
	println('     V + Redis Namespaced Helper Easy Demo        ')
	println('==================================================')

	println('Connecting to local Redis at 127.0.0.1:6379...')
	mut client := vredis.new_client(host: '127.0.0.1', port: 6379) or {
		eprintln('\n[ERROR] Failed to connect to Redis server: ${err}')
		eprintln('Please make sure Redis is running locally on port 6379.')
		return
	}
	defer {
		client.close() or {}
		println('\n==================================================')
		println('Demo completed. Redis connection closed.')
		println('==================================================')
	}

	println('Connected successfully!\n')

	// Create a namespaced client wrapper for "cache"
	println('Initializing "cache" namespace wrapper...')
	mut cache := new_namespaced_redis(client, 'cache')

	// Create another namespaced client wrapper for "session"
	println('Initializing "session" namespace wrapper...\n')
	mut session := new_namespaced_redis(client, 'session')

	// 1. Store value in cache namespace (key will be "cache:user_123")
	println('1. Storing data in "cache" namespace (key: "user_123")...')
	cache.set('user_123', '{"name": "Alice", "role": "Admin"}') or { panic(err) }

	// 2. Store value in session namespace (key will be "session:user_123")
	println('2. Storing data in "session" namespace (key: "user_123")...')
	session.set('user_123', 'active_session_token_xyz987') or { panic(err) }

	println('\n--- Retrieval ---')

	// 3. Retrieve values using the namespace helpers
	cache_val := cache.get('user_123') or { panic(err) }
	session_val := session.get('user_123') or { panic(err) }

	println('Retrieved from cache:   "${cache_val}"')
	println('Retrieved from session: "${session_val}"')

	println('\n--- Verification (Direct Raw Lookups) ---')

	// 4. Retrieve values using the raw client directly to show the actual keys stored
	raw_cache := client.get('cache:user_123') or { panic(err) }
	raw_session := client.get('session:user_123') or { panic(err) }
	println('Raw key "cache:user_123" directly:   "${raw_cache}"')
	println('Raw key "session:user_123" directly: "${raw_session}"')

	// Cleanup
	println('\nCleaning up keys...')
	cache.del('user_123') or {}
	session.del('user_123') or {}
	println('Cleanup done.')
}

Redis Webview Demo

Redis Webview Demo

Modules help modularize V projects, managing imports and symbol visibility. This lesson on Redis Webview Demo demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

v
Run
module main

import json
import ttytm.webview
import xiusin.vredis

struct KeyInfo {
	name  string
	@type string
	ttl   int
}

struct KeyDetail {
mut:
	name     string
	@type    string
	ttl      int
	value    string
	list_val []string
	hash_val map[string]string
}

struct ConnectStatus {
	status     string
	host       string
	port       int
	version    string
	keys_count int
}

// Embed the HTML, CSS, and JS file directly into the binary
const html_file = $embed_file('index.html')
const html = html_file.to_string()

fn connect_redis() !&vredis.Redis {
	return vredis.new_client(host: '127.0.0.1', port: 6379)
}

fn redis_connect_status(e &webview.Event) !string {
	mut client := connect_redis() or {
		status_info := ConnectStatus{
			status: 'disconnected'
			host: '127.0.0.1'
			port: 6379
			version: ''
			keys_count: 0
		}
		return json.encode(status_info)
	}
	defer {
		client.close() or {}
	}

	mut version := 'Unknown'
	info := client.send('INFO', 'server') or {
		count := client.dbsize() or { 0 }
		status_info := ConnectStatus{
			status: 'connected'
			host: '127.0.0.1'
			port: 6379
			version: 'Unknown'
			keys_count: count
		}
		return json.encode(status_info)
	}
	if info.bytestr().len > 0 {
		lines := info.bytestr().split('\n')
		for line in lines {
			if line.starts_with('redis_version:') {
				parts := line.split(':')
				if parts.len >= 2 {
					version = parts[1].trim_space()
				}
				break
			}
		}
	}

	count := client.dbsize() or { 0 }

	status_info := ConnectStatus{
		status: 'connected'
		host: '127.0.0.1'
		port: 6379
		version: version
		keys_count: count
	}
	return json.encode(status_info)
}

fn redis_get_keys(e &webview.Event) !string {
	mut client := connect_redis()!
	defer {
		client.close() or {}
	}

	keys := client.keys('*') or { []string{} }
	mut items := []KeyInfo{}
	for key in keys {
		t := client.@type(key) or { 'unknown' }
		ttl := client.ttl(key) or { -1 }
		items << KeyInfo{
			name: key
			@type: t
			ttl: ttl
		}
	}
	return json.encode(items)
}

fn redis_get_key_detail(e &webview.Event) !string {
	mut client := connect_redis()!
	defer {
		client.close() or {}
	}

	key := e.get_arg[string](0)!
	t := client.@type(key)!
	ttl := client.ttl(key)!

	mut detail := KeyDetail{
		name: key
		@type: t
		ttl: ttl
		value: ''
		list_val: []string{}
		hash_val: map[string]string{}
	}

	match t {
		'string' {
			detail.value = client.get(key) or { '' }
		}
		'list' {
			detail.list_val = client.lrange(key, 0, -1) or { []string{} }
		}
		'set' {
			detail.list_val = client.smembers(key) or { []string{} }
		}
		'hash' {
			detail.hash_val = client.hgetall(key) or { map[string]string{} }
		}
		else {}
	}
	return json.encode(detail)
}

fn redis_set_string(e &webview.Event) !string {
	mut client := connect_redis()!
	defer {
		client.close() or {}
	}

	key := e.get_arg[string](0)!
	val := e.get_arg[string](1)!
	ttl := e.get_arg[int](2)!

	client.set(key, val)!
	if ttl > 0 {
		client.expire(key, ttl)!
	} else if ttl == -1 {
		client.persist(key) or {}
	}
	return 'ok'
}

fn redis_set_list(e &webview.Event) !string {
	mut client := connect_redis()!
	defer {
		client.close() or {}
	}

	key := e.get_arg[string](0)!
	vals_json := e.get_arg[string](1)!
	ttl := e.get_arg[int](2)!

	vals := json.decode([]string, vals_json)!
	client.del(key) or {}
	for val in vals {
		client.rpush(key, val)!
	}
	if ttl > 0 {
		client.expire(key, ttl)!
	} else if ttl == -1 {
		client.persist(key) or {}
	}
	return 'ok'
}

fn redis_set_hash(e &webview.Event) !string {
	mut client := connect_redis()!
	defer {
		client.close() or {}
	}

	key := e.get_arg[string](0)!
	hash_json := e.get_arg[string](1)!
	ttl := e.get_arg[int](2)!

	fvs := json.decode(map[string]string, hash_json)!
	client.del(key) or {}
	for field, val in fvs {
		client.hset(key, field, val)!
	}
	if ttl > 0 {
		client.expire(key, ttl)!
	} else if ttl == -1 {
		client.persist(key) or {}
	}
	return 'ok'
}

fn redis_set_set(e &webview.Event) !string {
	mut client := connect_redis()!
	defer {
		client.close() or {}
	}

	key := e.get_arg[string](0)!
	vals_json := e.get_arg[string](1)!
	ttl := e.get_arg[int](2)!

	vals := json.decode([]string, vals_json)!
	client.del(key) or {}
	for val in vals {
		client.sadd(key, val)!
	}
	if ttl > 0 {
		client.expire(key, ttl)!
	} else if ttl == -1 {
		client.persist(key) or {}
	}
	return 'ok'
}

fn redis_del_key(e &webview.Event) !string {
	mut client := connect_redis()!
	defer {
		client.close() or {}
	}

	key := e.get_arg[string](0)!
	client.del(key)!
	return 'ok'
}

fn redis_flush_db(e &webview.Event) !string {
	mut client := connect_redis()!
	defer {
		client.close() or {}
	}

	client.flushdb()!
	return 'ok'
}

fn main() {
	mut w := webview.create(debug: true)
	defer {
		w.destroy()
	}
	w.set_title('V + Redis GUI Dashboard')
	w.set_size(1080, 720, .@none)

	// Bindings
	w.bind_opt[string]('redis_connect_status', redis_connect_status)
	w.bind_opt[string]('redis_get_keys', redis_get_keys)
	w.bind_opt[string]('redis_get_key_detail', redis_get_key_detail)
	w.bind_opt[string]('redis_set_string', redis_set_string)
	w.bind_opt[string]('redis_set_list', redis_set_list)
	w.bind_opt[string]('redis_set_hash', redis_set_hash)
	w.bind_opt[string]('redis_set_set', redis_set_set)
	w.bind_opt[string]('redis_del_key', redis_del_key)
	w.bind_opt[string]('redis_flush_db', redis_flush_db)

	w.set_html(html)
	w.run()
}

Webview Demo

Webview Demo

Modules help modularize V projects, managing imports and symbol visibility. This lesson on Webview Demo demonstrates code structure, module namespaces, access modifiers, or lifecycle rules.

Additional Context from Repository docs:

This example demonstrates the concepts of installing external packages and webview bindings.

v
Run
module main

import ttytm.webview

const html = '
<!DOCTYPE html>
<html>
<head>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background: linear-gradient(135deg, #1e1e2f 0%, #111119 100%);
            color: #f8f8f2;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            margin: 0;
            user-select: none;
        }
        .container {
            text-align: center;
            background: rgba(255, 255, 255, 0.05);
            padding: 30px;
            border-radius: 12px;
            box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.3);
            backdrop-filter: blur(4px);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }
        h1 {
            margin-bottom: 20px;
            font-size: 2.2rem;
            color: #50fa7b;
        }
        input {
            padding: 10px 15px;
            font-size: 1rem;
            border-radius: 6px;
            border: 1px solid #6272a4;
            background-color: #282a36;
            color: #f8f8f2;
            margin-right: 10px;
            outline: none;
        }
        button {
            padding: 10px 20px;
            font-size: 1rem;
            font-weight: bold;
            color: #282a36;
            background-color: #50fa7b;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            transition: all 0.2s ease;
        }
        button:hover {
            background-color: #8be9fd;
            transform: translateY(-1px);
        }
        #result {
            margin-top: 25px;
            font-size: 1.1rem;
            min-height: 25px;
            color: #f1fa8c;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>V + Webview Binding</h1>
        <input type="text" id="userInput" placeholder="Enter message for V..." value="Hello from JS!">
        <button onclick="sendToV()">Send to V</button>
        <div id="result">Waiting for action...</div>
    </div>

    <script>
        async function sendToV() {
            const input = document.getElementById("userInput").value;
            const resultDiv = document.getElementById("result");
            resultDiv.innerText = "Calling V function...";
            try {
                // Call the bound V function "greet_from_v" asynchronously
                const res = await window.greet_from_v(input);
                resultDiv.innerText = res;
            } catch (err) {
                resultDiv.innerText = "Error: " + err;
            }
        }
    </script>
</body>
</html>
'

// V binding function. Must take &webview.Event and can return a type (like string).
fn greet_from_v(e &webview.Event) string {
    // 1. Retrieve the argument passed from JavaScript (at index 0)
    msg := e.get_arg[string](0) or { 'No arguments passed' }
    println('V side: Received from JS: ${msg}')

    // 2. We can run custom JavaScript on the webview page from V
    e.eval('console.log("V successfully invoked eval in JS context!");')

    // 3. Return string back to the JS Promise resolver
    return 'V responds: "Message received: ${msg}"'
}

fn main() {
    // Initialize Webview
    mut w := webview.create(debug: true)
    w.set_title('V Webview Binding Demo')
    w.set_size(600, 450, .@none)

    // Bind V function "greet_from_v" to JS window.greet_from_v
    w.bind('greet_from_v', greet_from_v)

    // Load the HTML content
    w.set_html(html)

    // Run the main loop
    w.run()
}

Chapter 10 Writing Tests in 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:

Assertions & Unit Testing


V has testing built directly into the compiler. This chapter explains how to write test files, use assertions, set up test suites with setup/teardown methods, and run test suites.

Assertions & Unit Testing

Assert Demo

Assert Demo

V has built-in testing support. Any file ending with test.v is considered a test file. Inside test files, you write functions starting with test and use assert statements to check if conditions are true. You can run all tests in a folder using the v test . command.

These examples cover writing simple assertions, test suites, and testing functions that return options or errors.

Additional Context from Repository docs:

This example demonstrates the concepts of assert demo.

v
Run
module main

fn main() {
	println('1st assert')
	msg := 'hello there!'
	assert msg.contains('hello') // true
	println('2nd assert')
	assert 'apple' == 'orange' // stops execution
	println('done')
}

Simple Test - Before (demo_test.v)

Simple Test - Before

V has built-in testing support. Any file ending with test.v is considered a test file. Inside test files, you write functions starting with test and use assert statements to check if conditions are true. You can run all tests in a folder using the v test . command.

These examples cover writing simple assertions, test suites, and testing functions that return options or errors.

Additional Context from Repository docs:

This example demonstrates the concepts of demo test.

v
Run
fn test_first() {
	assert 2 != 2
}

Simple Test - After (demo_test.v)

Simple Test - After

V has built-in testing support. Any file ending with test.v is considered a test file. Inside test files, you write functions starting with test and use assert statements to check if conditions are true. You can run all tests in a folder using the v test . command.

These examples cover writing simple assertions, test suites, and testing functions that return options or errors.

Additional Context from Repository docs:

This example demonstrates the concepts of demo test.

v
Run
fn test_first() {
	assert 2 == 2
}

Testsuite Demo Test

Testsuite Demo Test

V has built-in testing support. Any file ending with test.v is considered a test file. Inside test files, you write functions starting with test and use assert statements to check if conditions are true. You can run all tests in a folder using the v test . command.

These examples cover writing simple assertions, test suites, and testing functions that return options or errors.

Additional Context from Repository docs:

This example demonstrates the concepts of testsuite demo test.

v
Run
import os

fn testsuite_begin() {
	os.setenv('foo', 'bar', true)
	println('About to start executing all tests')
}

fn test_env_foo_has_value_bar() {
	println('Executing test')

	// arrange
	inp := 'foo'
	expected := 'bar'

	// act
	actual := os.getenv(inp)

	// assert
	assert actual == expected
}

fn testsuite_end() {
	os.unsetenv('foo')
	println('Finished executing all tests')
}

Testing Optional Return Functions (demo_test.v)

Testing Optional Return Functions

V has built-in testing support. Any file ending with test.v is considered a test file. Inside test files, you write functions starting with test and use assert statements to check if conditions are true. You can run all tests in a folder using the v test . command.

These examples cover writing simple assertions, test suites, and testing functions that return options or errors.

Additional Context from Repository docs:

This example demonstrates the concepts of demo test.

v
Run
fn greet(name string) !string {
	if name != '' {
		return 'Hello $name!'
	}
	return error('name not provided')
}

fn test_greet_given_a_name() {
	exp := 'Hello Pavan!'
	assert (greet('Pavan') or { err.msg() }) == exp
}

fn test_greet_propagates_error() ! {
	greet('')!
}

fn test_greet_when_empty() {
	exp := 'name not provided'
	assert (greet('') or { err.msg() }) == exp
}

Greet

Greet

V has built-in testing support. Any file ending with test.v is considered a test file. Inside test files, you write functions starting with test and use assert statements to check if conditions are true. You can run all tests in a folder using the v test . command.

These examples cover writing simple assertions, test suites, and testing functions that return options or errors.

Additional Context from Repository docs:

This example demonstrates the concepts of greet.

v
Run
module main

fn greet(name string) string {
	return 'Hello $name!'
}

fn main() {
	msg := greet('Bob')
	println(msg)
}

Greet Test

Greet Test

V has built-in testing support. Any file ending with test.v is considered a test file. Inside test files, you write functions starting with test and use assert statements to check if conditions are true. You can run all tests in a folder using the v test . command.

These examples cover writing simple assertions, test suites, and testing functions that return options or errors.

Additional Context from Repository docs:

This example demonstrates the concepts of greet test.

v
Run
module main

fn test_greet() {
	// Arrange
	name := 'Bob'
	exp_msg := 'Hello Bob!'

	// Act
	act_msg := greet(name)

	// Assert
	assert act_msg == exp_msg
	assert act_msg.contains(name)
}

Main Test

Main Test

V has built-in testing support. Any file ending with test.v is considered a test file. Inside test files, you write functions starting with test and use assert statements to check if conditions are true. You can run all tests in a folder using the v test . command.

These examples cover writing simple assertions, test suites, and testing functions that return options or errors.

Additional Context from Repository docs:

This example demonstrates the concepts of main test.

v
Run
module main

import mod1

fn test_hello() {
	// arrange
	exp := 'Hello from mod1!'

	// act
	act := mod1.hello()

	// assert
	assert act == exp
	assert mod1.hello().contains('Hello')
}

Testing Program Modules - Helper (file1.v)

Testing Program Modules - Helper

V has built-in testing support. Any file ending with test.v is considered a test file. Inside test files, you write functions starting with test and use assert statements to check if conditions are true. You can run all tests in a folder using the v test . command.

These examples cover writing simple assertions, test suites, and testing functions that return options or errors.

Additional Context from Repository docs:

This example demonstrates the concepts of file1.

v
Run
module mod1

pub fn hello() string {
	return 'Hello from mod1!'
}

Mod1 Test

Mod1 Test

V has built-in testing support. Any file ending with test.v is considered a test file. Inside test files, you write functions starting with test and use assert statements to check if conditions are true. You can run all tests in a folder using the v test . command.

These examples cover writing simple assertions, test suites, and testing functions that return options or errors.

Additional Context from Repository docs:

This example demonstrates the concepts of mod1 test.

v
Run
module mod1

fn test_hello() {
	// arrange
	exp := 'Hello from mod1!'

	// act
	act := hello()

	// assert
	assert act == exp
}

Modulebasics

Modulebasics

V has built-in testing support. Any file ending with test.v is considered a test file. Inside test files, you write functions starting with test and use assert statements to check if conditions are true. You can run all tests in a folder using the v test . command.

These examples cover writing simple assertions, test suites, and testing functions that return options or errors.

Additional Context from Repository docs:

This example demonstrates the concepts of modulebasics.

v
Run
module main

import mod1

fn main() {
	res := mod1.hello()
	println(res)
}

Chapter 11 Concurrency and Channels

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:

Channels & Communication

V-Routines & Concurrency


V makes concurrent programming easy and safe. This chapter covers spawning threads using spawn, communicating safely between threads using channels, and sharing state safely using shared and lock primitives.

Channels & Communication

Unbuffered Channel

Unbuffered Channel

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of unbuffered channel.

v
Run
fn main() {
	uc := chan int{}
	println(uc.cap) // 0
	println(typeof(uc).name) // chan int
}

Define Buffered Channel (buffered_channel.v)

Define Buffered Channel

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of buffered channel.

v
Run
fn main() {
	bc := chan string{cap: 2}
	println(bc.cap)
	println(typeof(bc).name)
}

Push Buffered

Push Buffered

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of push buffered.

v
Run
fn main() {
	ch := chan int{cap: 1}
	ch <- 51
	println(ch)
}

Push Unbuffered

Push Unbuffered

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of push unbuffered.

v
Run
fn main() {
	ch := chan int{}
	ch <- 51
	println(ch) // doesn't prints, due to blocking behavior of unbuffered channels
}

Pop

Pop

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of pop.

v
Run
fn main() {
	ch := chan int{cap: 1}
	ch <- 51
	println('channel after push: ${ch.str()}')

	println('popping value out of the channel and storing it in immutable variable x')
	x := <-ch
	println('value of x: ${x}')
	println('channel after pop: ${ch.str()}')
}

Channel Properties

Channel Properties

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of channel properties.

v
Run
fn main() {
	b := chan string{cap: 2}
	b <- 'hello'
	println('capacity: ${b.cap}')
	println('length: ${b.len}')
	println('closed: ${b.closed}')
}

Try Push Unbuffered

Try Push Unbuffered

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of try push unbuffered.

v
Run
fn main() {
	v := 'hi'
	ch := chan string{} // unbuffered channel
	res := ch.try_push(v)
	println(res) // not_ready
}

Try Push Buffered

Try Push Buffered

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of try push buffered.

v
Run
fn main() {
	x := 'hello'
	ch := chan string{cap: 2}
	for {
		status := ch.try_push(x)
		if status == .success {
			println('Channel length: ${ch.len}')
		} else {
			println('channel status: ${status}')
			break
		}
	}
}

Try Pop

Try Pop

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of try pop.

v
Run
fn main() {
	ch := chan int{cap: 1}
	mut x, mut y := 0, 0
	ch <- 101
	mut status := ch.try_pop(mut x)
	println('try pop resulted in status: ${status}, Value of x: ${x}')
	status = ch.try_pop(mut y)
	println('try pop resulted in status: ${status}, Value of y: ${y}')
}

Close

Close

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of close.

v
Run
module main

fn main() {
	ch := chan int{cap: 2}

	// push using arrow operator: <-
	ch <- 123 // Push 1st element into the channel
	ch <- 222 // Push 2nd element into the channel
	println(<-ch) // pop using: <- First in is the first to out. So prints 123
	ch.close() // Close channel

	// try_push will result .closed
	new_val := 999
	status := ch.try_push(new_val)
	println('try_push on a closed channel resulted in status: ${status}')

	// We still have one more element to pop
	println(<-ch) // 222
}

Defer Close

Defer Close

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of defer close.

v
Run
module main

fn main() {
	ch := chan int{cap: 2}
	defer {
		ch.close()
	} // Deferred execution to Close channel

	// push using arrow operator: <-
	ch <- 123 // Push 1st element into the channel
	ch <- 222 // Push 2nd element into the channel
	println(<-ch) // pop using: <- First in is the first to out. So prints 123

	// try_push will result .closed
	new_val := 999
	status := ch.try_push(new_val)
	println('try_push on a closed channel resulted in status: ${status}')

	// We still have one more element to pop
	println(<-ch) // 222
}

Blocking Channels

Blocking Channels

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of blocking channels.

v
Run
module main

fn main() {
	ch := chan int{}
	defer {
		ch.close()
	}
	ch <- 3
	x := <-ch
	println(x)
	println('End main')
}

Dealing Before

Dealing Before

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of dealing before.

v
Run
module main

fn receiver(ch chan int) {
	println('Received value from the channel ${<-ch}')
}

fn main() {
	ch := chan int{}
	defer {
		ch.close()
	}
	go receiver(ch)
	ch <- 3
	println('End main')
}

Dealing After

Dealing After

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of dealing after.

v
Run
module main

fn receiver(ch chan int) {
	println('Received value from the channel ${<-ch}')
}

fn main() {
	ch := chan int{}
	defer {
		ch.close()
	}
	t := go receiver(ch)
	ch <- 3
	t.wait()
	println('End main')
}

Unbuffered Sync Before (sync_before.v)

Unbuffered Sync Before

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of sync before.

v
Run
module main

const count = 4

fn sender(ch chan int) {
	for i in 0 .. count {
		ch <- i // since the push operation is a void expression, this cannot be placed in a println
		println('Sent ${i} into the channel')
	}
}

fn receiver(ch chan int) {
	println('Received value from the channel ${<-ch}')
}

fn main() {
	ch := chan int{}
	defer {
		ch.close()
	}
	t := go receiver(ch)
	go sender(ch)
	t.wait()
	println('End main')
}

Unbuffered Sync After (sync_after.v)

Unbuffered Sync After

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of sync after.

v
Run
module main

const count = 4

fn sender(ch chan int) {
	for i in 0 .. count {
		ch <- i // since the push operation is a void expression, this cannot be placed in a println
		println('Sent ${i} into the channel')
	}
}

fn receiver(ch chan int) {
	for _ in 0 .. count {
		println('Received value from the channel ${<-ch}')
	}
}

fn main() {
	ch := chan int{}
	defer {
		ch.close()
	}
	t := go receiver(ch)
	go sender(ch)
	t.wait()
	println('End main')
}

Understanding Buffered Channel (buffered_channel.v)

Understanding Buffered Channel

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of buffered channel.

v
Run
module main

fn main() {
	ch := chan int{cap: 1}
	defer {
		ch.close()
	}
	ch <- 3
	x := <-ch
	println(x)
	println('End main')
}

Coroutines Communication

Coroutines Communication

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of coroutines communication.

v
Run
module main

fn sender(ch chan int) {
	val := 3
	println('Sending value: ${val} in the channel')
	ch <- val
	println('sent value: ${val} in the channel')
}

fn receiver(ch chan int) {
	println('Received value from the channel ${<-ch}')
}

fn main() {
	ch := chan int{cap: 1}
	defer {
		ch.close()
	}
	t := go receiver(ch)
	go sender(ch)

	t.wait()
	println('End main')
}

Buffered Sync Before (sync_before.v)

Buffered Sync Before

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of sync before.

v
Run
module main

const count = 4

fn sender(ch chan int) {
	for i in 0 .. count {
		ch <- i
		println('sent value: ${i} in the channel')
	}
}

fn receiver(ch chan int) {
	println('Received value from the channel ${<-ch}')
}

fn main() {
	ch := chan int{cap: 2}
	defer {
		ch.close()
	}
	t := go receiver(ch)
	go sender(ch)

	t.wait()
	println('End main')
}

Buffered Sync After (sync_after.v)

Buffered Sync After

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of sync after.

v
Run
module main

const count = 4

fn sender(ch chan int) {
	for i in 0 .. count {
		ch <- i
		println('sent value: ${i} in the channel')
	}
}

fn receiver(ch chan int) {
	for _ in 0 .. count {
		println('Received value from the channel ${<-ch}')
	}
}

fn main() {
	ch := chan int{cap: 2}
	defer {
		ch.close()
	}
	t := go receiver(ch)
	go sender(ch)

	t.wait()
	println('End main')
}

Channel Select Before

Channel Select Before

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of channel select before.

v
Run
module main

fn process1(ch chan int) {
	for i in 1 .. 6 {
		sq := i * i
		println('process1: value being pushed on ch1: ${sq}')
		ch <- sq
	}
}

fn process2(ch chan string) {
	msg := 'hello from process 2'
	println('process2: value being pushed on ch2: ${msg}')
	ch <- msg
}

fn main() {
	ch1 := chan int{cap: 5} // buffered channel
	ch2 := chan string{} // unbuffered channel
	defer {
		ch1.close()
		ch2.close()
	}
	go process1(ch1)
	go process2(ch2)
	select {
		a := <-ch1 {
			println('main: value popped from ch1: ${a}')
		}
		b := <-ch2 {
			println('main: value popped from ch2: ${b}')
		}
	}
}

Channel Select

Channel Select

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of channel select.

v
Run
module main

import time

fn process1(ch chan int) {
	for i in 1 .. 6 {
		sq := i * i
		time.sleep(3 * time.second)
		println('process1: value being pushed on ch1: ${sq}')
		ch <- sq
	}
}

fn process2(ch chan string) {
	msg := 'hello from process 2'
	println('process2: value being pushed on ch2: ${msg}')
	ch <- msg
}

fn main() {
	ch1 := chan int{cap: 5} // buffered channel
	ch2 := chan string{} // unbuffered channel
	defer {
		ch1.close()
		ch2.close()
	}
	go process1(ch1)
	go process2(ch2)
	mut sec := 0
	for {
		select {
			a := <-ch1 {
				sec = 0
				println('main: value popped from ch1: ${a}')
			}
			b := <-ch2 {
				sec = 0
				println('main: value popped from ch2: ${b}')
			}
			2 * time.second {
				// this case executes for every 2 seconds of inactivity by any other channels in this select statement
				sec = sec + 2
				println('main: more than ${sec}s passed without a channel being ready')
				if sec >= 6 {
					println('exiting out of select after ${sec} seconds of inactivity amongst channels')
					break
				}
			}
		}
	}
	println('done')
}

V-Routines & Concurrency

Stopwatch Demo

Stopwatch Demo

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of stopwatch demo.

v
Run
module main

import time

fn main() {
	sw := time.new_stopwatch()

	for i in 1 .. 5 {
		println('$i')
	}
	println('Total time took to finish: $sw.elapsed().seconds() seconds')
}

Spawn Void Function

Spawn Void Function

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of spawn void function.

v
Run
module main

fn greet() {
	println('Hello from other side!')
}

fn main() {
	h := go greet()
	println(typeof(h).name) // thread
}

Waiting On Concurrent Thread

Waiting On Concurrent Thread

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of waiting on concurrent thread.

v
Run
module main

fn greet() {
	println('Hello from other side!')
}

fn main() {
	h := go greet()
	println(typeof(h).name)
	h.wait()
}

Running Multiple Tasks In Sequence

Running Multiple Tasks In Sequence

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of running multiple tasks in sequence.

v
Run
module main

import time

fn hot_water() {
	println('Started Switch on Water heater: $time.now().hhmmss()')
	time.sleep(5 * time.second)
	println('Water heater indicates hot water ready!: $time.now().hhmmss()')
}

fn brush_teeth() {
	println('Started brushing:  $time.now().hhmmss()')
	time.sleep(3 * time.second)
	println('End Brushing:  $time.now().hhmmss()')
}

fn select_clothes() {
	println('Started choosing pair of clothes :  $time.now().hhmmss()')
	time.sleep(3 * time.second)
	println('End choosing pair of clothes:  $time.now().hhmmss()')
}

fn main() {
	sw := time.new_stopwatch()
	hot_water()
	brush_teeth()
	select_clothes()
	println('Your pre bath morning chores took: $sw.elapsed().seconds() seconds')
}

Spawning Multiple Tasks Concurrently

Spawning Multiple Tasks Concurrently

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of spawning multiple tasks concurrently.

v
Run
module main

import time

fn hot_water() {
	println('Started Switch on Water heater: $time.now().hhmmss()')
	time.sleep(5 * time.second)
	println('Water heater indicates hot water ready! : $time.now().hhmmss()')
}

fn brush_teeth() {
	println('Started brushing:  $time.now().hhmmss()')
	time.sleep(3 * time.second)
	println('End Brushing:  $time.now().hhmmss()')
}

fn select_clothes() {
	println('Started choosing pair of clothes:  $time.now().hhmmss()')
	time.sleep(3 * time.second)
	println('End choosing pair of clothes:  $time.now().hhmmss()')
}

fn main() {
	mut t := []thread{}
	sw := time.new_stopwatch()
	t << go hot_water()
	t << go brush_teeth()
	t << go select_clothes()
	t.wait()
	println('Your pre bath morning chores took: $sw.elapsed().seconds() seconds')
}

Functions With Return Values

Functions With Return Values

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of functions with return values.

v
Run
module main

import time

fn hot_water() string {
	println('Started Switch on Water heater: $time.now().hhmmss()')
	time.sleep(5 * time.second)
	println('Water heater indicates hot water ready! : $time.now().hhmmss()')
	return 'Hot water ready!'
}

fn brush_teeth() string {
	println('Started brushing:  $time.now().hhmmss()')
	time.sleep(3 * time.second)
	println('End Brushing:  $time.now().hhmmss()')
	return 'Sparkling Teeth ready!'
}

fn select_clothes() string {
	println('Started choosing pair of clothes:  $time.now().hhmmss()')
	time.sleep(3 * time.second)
	println('End choosing pair of clothes:  $time.now().hhmmss()')
	return 'Pair of clothes ready!'
}

fn main() {
	mut t := []thread string{}
	sw := time.new_stopwatch()
	t << go hot_water()
	t << go brush_teeth()
	t << go select_clothes()
	res := t.wait()
	println('Your pre bath morning chores took: $sw.elapsed().seconds() seconds')
	println('*** Type Check ***')
	println('Type of thread array of strings t: ${typeof(t).name}')
	println('Type of res: ${typeof(res).name}')
	println('*** Values returned by concurrently executed tasks ***')
	println(res)
}

Spawn Anonymous Funcs Without Input Args

Spawn Anonymous Funcs Without Input Args

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of spawn anonymous funcs without input args.

v
Run
module main

fn main() {
	t := go fn () string {
		return 'hi'
	}()
	x := t.wait()
	println(typeof(x).name) // string
	println(x) // hi
}

Spawn Anonymous Funcs With Input Args

Spawn Anonymous Funcs With Input Args

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of spawn anonymous funcs with input args.

v
Run
module main

fn main() {
	mut t := []thread string{}
	for i in 1 .. 3 {
		t << go fn (i int, msg string) string {
			return 'iteration: $i, message: $msg'
		}(i, 'hello') // <- arguments must match list in the anonymous function definition
	}
	res := t.wait()
	println('Type of t: ${typeof(t).name}')
	println('Type of res: ${typeof(res).name}')
	println(res)
}

Sharing Data Main And Concurrent Tasks

Sharing Data Main And Concurrent Tasks

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of sharing data main and concurrent tasks.

v
Run
module main

import rand

struct Fund {
	name   string
	target f32
mut:
	total      f32
	num_donors int
}

fn (shared f Fund) collect(amt f32) {
	lock f { // read - write lock
		if f.total < f.target {
			f.num_donors += 1
			f.total += amt
			println('$f.num_donors \t before: ${f.total - amt} \t funds received: $amt \t total: $f.total')
		}
	}
}

fn donation() f32 {
	return rand.f32_in_range(100.00, 250.00) or { 100.00 }
}

fn main() {
	shared fund := Fund{
		name: 'A noble cause'
		target: 1000.00
	}

	for {
		rlock fund {
			if fund.total >= fund.target {
				break
			}
		}
		h := go donation()
		go fund.collect(h.wait())
	}

	rlock fund { // acquire read lock
		println('$fund.num_donors donors donated for $fund.name')
		println('$fund.name raised total fund amount: \$ $fund.total')
	}
}

Chapter 12 Working with Databases and JSON

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:

Case Study: Notes API

JSON & ORM

SQLite Integration

Sqlite Raw Crud


Most applications need to work with databases or API payloads. This chapter teaches you how to serialize and deserialize JSON data, use V's built-in ORM with SQLite, and covers a complete Notes REST API case study.

Case Study: Notes API

Notes API Case Study - Main (main.v)

Notes API Case Study - Main

This is a complete, real-world case study of a REST API built using the V web framework (veb). It includes routing, JSON requests/responses, and persistence using SQLite. It is a great example of how all the pieces of V fit together to build a production-grade application.

Additional Context from Repository docs:

This example demonstrates the concepts of main.

v
Run
module main

import veb
import db.sqlite

struct App {
mut:
	db sqlite.DB
}

struct Context {
	veb.Context
}

fn main() {
	mut db := sqlite.connect('notes.db') or { panic(err) }
	defer {
		db.close() or {}
	}
	db.exec('drop table if exists Notes') or { panic(err) }
	sql db {
		create table Note
	} or { panic(err) }
	http_port := 8000
	mut app := &App{
		db: db
	}
	veb.run[App, Context](mut app, http_port)
}

Note

Note

This is a complete, real-world case study of a REST API built using the V web framework (veb). It includes routing, JSON requests/responses, and persistence using SQLite. It is a great example of how all the pieces of V fit together to build a production-grade application.

Additional Context from Repository docs:

This example demonstrates the concepts of note.

v
Run
module main

import json
import veb

@[table: 'Notes']
struct Note {
	id      int    @[primary; sql: serial]
	message string @[sql: 'detail'; unique]
	status  bool
}

fn (n Note) to_json() string {
	return json.encode(n)
}

@['/notes'; post]
fn (mut app App) create(mut ctx Context) veb.Result {
	// malformed json
	n := json.decode(Note, ctx.req.data) or {
		ctx.res.set_status(.bad_request)
		return ctx.json(error_response(400, invalid_json))
	}

	// before we save, we must ensure the note's message is unique
	notes_found := sql app.db {
		select from Note where message == n.message
	} or {
		ctx.res.set_status(.internal_server_error)
		return ctx.json(error_response(500, err.msg()))
	}
	if notes_found.len > 0 {
		ctx.res.set_status(.bad_request)
		return ctx.json(error_response(400, unique_message))
	}

	// save to db
	sql app.db {
		insert n into Note
	} or {
		ctx.res.set_status(.internal_server_error)
		return ctx.json(error_response(500, err.msg()))
	}

	// retrieve the last id from the db to build full Note object
	new_id := app.db.last_id() as int

	// build new note object including the new_id and send it as JSON response
	note_created := Note{new_id, n.message, n.status}
	ctx.res.set_status(.created)
	ctx.res.header.add(.content_location, '/notes/${new_id}')
	return ctx.json(note_created.to_json())
}

@['/notes/:id'; get]
fn (mut app App) read(mut ctx Context, id int) veb.Result {
	n := sql app.db {
		select from Note where id == id
	} or {
		ctx.res.set_status(.internal_server_error)
		return ctx.json(error_response(500, err.msg()))
	}

	// check if note exists
	if n.len == 0 {
		ctx.res.set_status(.not_found)
		return ctx.json(error_response(400, note_not_found))
	}

	// found note, return it
	ret := json.encode(n[0])
	ctx.res.set_status(.ok)
	return ctx.json(ret)
}

@['/notes'; get]
fn (mut app App) read_all(mut ctx Context) veb.Result {
	n := sql app.db {
		select from Note
	} or {
		ctx.res.set_status(.internal_server_error)
		return ctx.json(error_response(500, err.msg()))
	}

	ret := json.encode(n)
	ctx.res.set_status(.ok)
	return ctx.json(ret)
}

@['/notes/:id'; put]
fn (mut app App) update(mut ctx Context, id int) veb.Result {
	// malformed json
	n := json.decode(Note, ctx.req.data) or {
		ctx.res.set_status(.bad_request)
		return ctx.json(error_response(400, invalid_json))
	}

	// check if note to be updated exists
	note_to_update := sql app.db {
		select from Note where id == id
	} or {
		ctx.res.set_status(.internal_server_error)
		return ctx.json(error_response(500, err.msg()))
	}

	if note_to_update.len == 0 {
		ctx.res.set_status(.not_found)
		return ctx.json(error_response(404, note_not_found))
	}

	// before update, we must ensure the note's message is unique
	// id != id for idempotency
	// message == n.message for unique check
	res := sql app.db {
		select from Note where message == n.message && id != id
	} or {
		ctx.res.set_status(.internal_server_error)
		return ctx.json(error_response(500, err.msg()))
	}

	if res.len > 0 {
		ctx.res.set_status(.bad_request)
		return ctx.json(error_response(400, unique_message))
	}

	// update the note
	sql app.db {
		update Note set message = n.message, status = n.status where id == id
	} or {
		ctx.res.set_status(.internal_server_error)
		return ctx.json(error_response(500, err.msg()))
	}

	// build the updated note using the :id and request body
	// instead of making one more db call
	updated_note := Note{id, n.message, n.status}

	ret := json.encode(updated_note)
	ctx.res.set_status(.ok)
	return ctx.json(ret)
}

@['/notes/:id'; delete]
fn (mut app App) delete(mut ctx Context, id int) veb.Result {
	sql app.db {
		delete from Note where id == id
	} or {
		ctx.res.set_status(.internal_server_error)
		return ctx.json(error_response(500, err.msg()))
	}
	ctx.res.set_status(.no_content)
	return ctx.ok('')
}

Util

Util

This is a complete, real-world case study of a REST API built using the V web framework (veb). It includes routing, JSON requests/responses, and persistence using SQLite. It is a great example of how all the pieces of V fit together to build a production-grade application.

Additional Context from Repository docs:

This example demonstrates the concepts of util.

v
Run
module main

import json

struct NotesResponse {
	status  int
	message string
}

fn (c NotesResponse) to_json() string {
	return json.encode(c)
}

const invalid_json = 'Invalid JSON Payload'
const note_not_found = 'Note not found'
const unique_message = 'Please provide a unique message for Note'

fn error_response(status int, message string) string {
	er := NotesResponse{status, message}
	return er.to_json()
}

JSON & ORM

Decode

Decode

Databases and JSON handling are essential parts of backend development. This lesson on Decode details V's built-in JSON utilities or its built-in database ORM.

Additional Context from Repository docs:

This example demonstrates the concepts of decode.

v
Run
import json

struct Note {
	id      int
	message string
	status  bool
}

fn main() {
	n := json.decode(Note, '{"id":1,"message":"Plan a holiday","status":false}') or {
		panic('invalid json data')
	}
	println(typeof(n).name) // Note
	println(n)
}

Encode

Encode

Databases and JSON handling are essential parts of backend development. This lesson on Encode details V's built-in JSON utilities or its built-in database ORM.

Additional Context from Repository docs:

This example demonstrates the concepts of encode.

v
Run
import json

struct Note {
	id      int
	message string
	status  bool
}

fn main() {
	m := Note{
		id: 2
		message: 'Get groceries'
		status: false
	}

	j := json.encode(m)
	println(j)
}

Json To From File

This example demonstrates how to encode an object to JSON, write it to a file, read it back, and decode it into a V struct.

v
Run
module main

import json
import os

struct Book {
	title  string
	author string
	year   int
}

fn main() {
	file_path := 'book.json'

	// Create an object instance
	book := Book{
		title: 'The V Programming Language'
		author: 'Alex Medvednikov'
		year: 2019
	}

	// 1. Encode object to JSON string
	println('Encoding object to JSON...')
	json_str := json.encode(book)
	println('JSON string: ${json_str}')

	// 2. Write JSON string to file
	println('Writing JSON to file "${file_path}"...')
	os.write_file(file_path, json_str) or {
		eprintln('Failed to write file: ${err}')
		return
	}

	// 3. Read JSON string from file
	println('Reading JSON from file "${file_path}"...')
	content := os.read_file(file_path) or {
		eprintln('Failed to read file: ${err}')
		return
	}

	// 4. Decode JSON string back to Book object
	println('Decoding JSON back to object...')
	decoded_book := json.decode(Book, content) or {
		eprintln('Failed to decode JSON: ${err}')
		return
	}

	println('Decoded book: Title: "${decoded_book.title}", Author: "${decoded_book.author}", Year: ${decoded_book.year}')

	// Clean up created file
	os.rm(file_path) or {}
}

Json Array Of Objects

This example demonstrates how to serialize and deserialize an array of objects (structs) to and from JSON, and how to write/read them using the filesystem.

v
Run
module main

import json
import os

struct Task {
	id    int
	title string
	done  bool
}

fn main() {
	file_path := 'tasks.json'

	// Create an array of objects
	tasks := [
		Task{
			id: 1
			title: 'Read V Guide'
			done: false
		},
		Task{
			id: 2
			title: 'Write JSON helper examples'
			done: true
		},
	]

	// 1. Encode array of objects to JSON string
	println('Encoding array of objects to JSON...')
	json_str := json.encode(tasks)
	println('JSON string:\n${json_str}')

	// 2. Write JSON string to file
	println('\nWriting JSON array to file "${file_path}"...')
	os.write_file(file_path, json_str) or {
		eprintln('Failed to write file: ${err}')
		return
	}

	// 3. Read JSON string from file
	println('Reading JSON from file "${file_path}"...')
	content := os.read_file(file_path) or {
		eprintln('Failed to read file: ${err}')
		return
	}

	// 4. Decode JSON string back to an array of Task objects
	println('Decoding JSON back to array of objects...')
	decoded_tasks := json.decode([]Task, content) or {
		eprintln('Failed to decode JSON: ${err}')
		return
	}

	println('Decoded array of tasks successfully!')
	for task in decoded_tasks {
		println('  - Task #${task.id}: "${task.title}" [Done: ${task.done}]')
	}

	// Clean up created file
	os.rm(file_path) or {}
}

Json Map To From File

This example demonstrates how to serialize a map structure (map[string]int) into a JSON string, write it to a file, read it back, and deserialize it back into a map in V.

v
Run
module main

import json
import os

fn main() {
	file_path := 'scores.json'

	// Create a map[string]int
	scores := {
		'Alice': 95
		'Bob': 88
		'Charlie': 92
	}

	// 1. Encode map to JSON string
	println('Encoding map to JSON...')
	json_str := json.encode(scores)
	println('JSON string: ${json_str}')

	// 2. Write JSON string to file
	println('Writing map JSON to file "${file_path}"...')
	os.write_file(file_path, json_str) or {
		eprintln('Failed to write file: ${err}')
		return
	}

	// 3. Read JSON string from file
	println('Reading from file "${file_path}"...')
	content := os.read_file(file_path) or {
		eprintln('Failed to read file: ${err}')
		return
	}

	// 4. Decode JSON string back to map[string]int
	println('Decoding JSON back to map...')
	decoded_scores := json.decode(map[string]int, content) or {
		eprintln('Failed to decode map JSON: ${err}')
		return
	}

	println('Decoded map successfully:')
	for k, v in decoded_scores {
		println('  - ${k}: ${v}')
	}

	// Clean up created file
	os.rm(file_path) or {}
}

Json Array To From File

This example demonstrates two different methods for reading and writing arrays to/from files in V:

  1. JSON Serialization: Best for primitive numeric/boolean arrays (e.g. []int).
  2. Raw Line-by-Line (Plain Text): Best for string lists (e.g. []string), joining with newlines on write and using V's standard os.read_lines() on read.
v
Run
module main

import json
import os

fn main() {
	// We will show two ways of writing/reading arrays to/from files:
	// Method 1: Using JSON serialization (great for numeric or structured arrays)
	// Method 2: Using raw text line-by-line reading/writing (great for string lists)

	// --- Method 1: JSON Serialization ---
	println('=== Method 1: JSON Serialization ===')
	json_file_path := 'numbers.json'
	numbers := [10, 20, 30, 40, 50]

	println('Encoding array to JSON...')
	json_str := json.encode(numbers)
	println('JSON string: ${json_str}')

	println('Writing JSON to file "${json_file_path}"...')
	os.write_file(json_file_path, json_str) or {
		eprintln('Failed to write file: ${err}')
		return
	}

	json_content := os.read_file(json_file_path) or {
		eprintln('Failed to read file: ${err}')
		return
	}

	decoded_numbers := json.decode([]int, json_content) or {
		eprintln('Failed to decode array JSON: ${err}')
		return
	}
	println('Decoded array: ${decoded_numbers}')
	os.rm(json_file_path) or {}

	// --- Method 2: Raw Line-by-Line (Plain Text) ---
	println('\n=== Method 2: Raw Line-by-Line ===')
	text_file_path := 'fruits.txt'
	fruits := ['Apple', 'Banana', 'Cherry', 'Date']

	println('Writing array elements to text file "${text_file_path}"...')
	// Join the string array with newlines to write line-by-line
	fruits_content := fruits.join('\n')
	os.write_file(text_file_path, fruits_content) or {
		eprintln('Failed to write file: ${err}')
		return
	}

	println('Reading lines from file "${text_file_path}" using os.read_lines()...')
	// os.read_lines reads a file directly into a []string (line by line)
	read_fruits := os.read_lines(text_file_path) or {
		eprintln('Failed to read lines: ${err}')
		return
	}
	println('Read string array: ${read_fruits}')

	// Clean up created files
	os.rm(text_file_path) or {}
}

Orm Demo

Orm Demo

Databases and JSON handling are essential parts of backend development. This lesson on Orm Demo details V's built-in JSON utilities or its built-in database ORM.

Additional Context from Repository docs:

This example demonstrates the concepts of orm demo.

v
Run
module main

import db.sqlite

@[table: 'Notes']
struct Note {
	id      int    @[primary; sql: serial]
	message string @[sql: 'detail'; unique]
	status  bool
}

fn main() {
	// Establishing a connection to the database

	mut db := sqlite.connect('NotesDB.db') or { panic(err) }
	defer {
		db.close() or {}
	}
	db.exec('drop table if exists Notes') or { panic(err) }

	// Creating a table
	sql db {
		create table Note
	} or { panic(err) }

	// Inserting record(s)
	n1 := Note{
		message: 'Get some milk'
		status: false
	}

	n2 := Note{
		message: 'Get groceries'
		status: false
	}
	sql db {
		insert n1 into Note
		insert n2 into Note
	} or { panic(err) }

	println(db.last_id() as int)

	// Select records
	all_notes := sql db {
		select from Note
	} or { panic(err) }

	println(all_notes)
	println('Type of all_notes is : ${typeof(all_notes).name}')

	// Select using order by clause
	notes_sorted := sql db {
		select from Note order by id desc
	} or { panic(err) }
	println(notes_sorted)

	// Select using the limit clause
	notes_limited := sql db {
		select from Note order by id desc limit 1
	} or { panic(err) }

	println(notes_limited)
	println('Type returned by select when limit is 1:  ${typeof(notes_limited).name}')

	// Select using where clause
	notes_latest := sql db {
		select from Note where id > 1
	} or { panic(err) }

	println(notes_latest)

	// Update record(s)
	sql db {
		update Note set status = true where id == 2
	} or { panic(err) }

	notes_updated := sql db {
		select from Note where id == 2
	} or { panic(err) }
	println(notes_updated)

	// Delete record(s)
	sql db {
		delete from Note where id == 2
	} or { panic(err) }

	notes_leftover := sql db {
		select from Note
	} or { panic(err) }
	println(notes_leftover)

	sql db {
		drop table Note
	} or { panic(err) }
	println('Dropped the Note table from database!')
}

SQLite Integration

Sqlite

Sqlite

Databases and JSON handling are essential parts of backend development. This lesson on Sqlite details V's built-in JSON utilities or its built-in database ORM.

v
Run
module sqlite

import db.sqlite as dbsqlite

pub type DB = dbsqlite.DB

pub fn connect(path string) !DB {
	return dbsqlite.connect(path)!
}

Sqlite Raw Crud

This example demonstrates how to connect to a SQLite database and execute raw SQL queries securely using parameterized queries (db.execparammany) to prevent SQL Injection attacks.

It illustrates:

  1. Connecting with sqlite.connect.
  2. Executing statements that do not return tabular results (like DDL/DML) via db.exec.
  3. Executing parameterized DML queries (INSERT, SELECT, UPDATE, DELETE) using db.execparammany and ? placeholders.
  4. Fetching result sets from SELECT statements into []sqlite.Row.
  5. Iterating and accessing row values from row.vals by index.
  6. Handling resource cleanup cleanly via defer.
TIP
SQL Injection Prevention: Avoid raw string interpolation (e.g. "$name") inside raw queries. Using db.execparammany forces parameter binding, rendering the query secure from malicious inputs.
v
Run
module main

import db.sqlite

fn main() {
	// 1. Database Connection
	// Connects to a SQLite database. ':memory:' creates a temporary in-memory database.
	println('Connecting to database...')
	mut db := sqlite.connect(':memory:') or {
		println('Connection failed: ${err}')
		return
	}
	defer {
		db.close() or { println('Failed to close database: ${err}') }
		println('Database connection closed.')
	}

	// 2. Schema Creation (DDL)
	println('Creating "users" table...')
	db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE, age INTEGER);') or {
		println('Table creation failed: ${err}')
		return
	}

	// 3. Create (Insert Records using Parameterized Queries)
	println('\n--- CREATE: Inserting records securely ---')

	// SAFE APPROACH: Use `exec_param_many` with '?' placeholders to prevent SQL Injection.
	// Parameters are passed as an array of strings: []string
	db.exec_param_many("INSERT INTO users (name, email, age) VALUES (?, ?, ?);", ['Alice', 'alice@example.com', '30']) or {
		println('Insert failed: ${err}')
	}
	db.exec_param_many("INSERT INTO users (name, email, age) VALUES (?, ?, ?);", ['Bob', 'bob@example.com', '25']) or {
		println('Insert failed: ${err}')
	}
	db.exec_param_many("INSERT INTO users (name, email, age) VALUES (?, ?, ?);", ['Charlie', 'charlie@example.com', '40']) or {
		println('Insert failed: ${err}')
	}

	println('Last inserted row ID: ${db.last_id()}')

	// 4. Read (Select Records using Parameterized Queries)
	println('\n--- READ: Querying records securely ---')

	// Querying with parameters: only retrieve users older than 20
	rows := db.exec_param_many('SELECT id, name, email, age FROM users WHERE age > ?;', ['20']) or {
		println('Select failed: ${err}')
		[]sqlite.Row{}
	}

	// Iterate and extract column values by index
	for row in rows {
		// Each sqlite.Row has two string arrays: `vals` (values) and `names` (column names)
		id := row.vals[0]
		name := row.vals[1]
		email := row.vals[2]
		age := row.vals[3]
		println('User [ID: ${id}] -> Name: ${name}, Email: ${email}, Age: ${age}')
	}

	// 5. Update (Modify Records using Parameterized Queries)
	println('\n--- UPDATE: Modifying Bob\'s email and age securely ---')
	db.exec_param_many("UPDATE users SET email = ?, age = ? WHERE name = ?;", ['bob_new@example.com', '26', 'Bob']) or {
		println('Update failed: ${err}')
	}

	// Verify update
	updated_rows := db.exec_param_many("SELECT email, age FROM users WHERE name = ?;", ['Bob']) or { []sqlite.Row{} }
	if updated_rows.len > 0 {
		println("Bob's new email: ${updated_rows[0].vals[0]}")
		println("Bob's new age:   ${updated_rows[0].vals[1]}")
	}

	// 6. Delete (Remove Records using Parameterized Queries)
	println('\n--- DELETE: Removing Charlie securely ---')
	db.exec_param_many("DELETE FROM users WHERE name = ?;", ['Charlie']) or {
		println('Delete failed: ${err}')
	}

	// Verify delete
	remaining_rows := db.exec('SELECT name FROM users;') or { []sqlite.Row{} }
	print('Remaining users: ')
	for row in remaining_rows {
		print('${row.vals[0]} ')
	}
	println('')

	// 7. Cleanup
	println('\nDropping "users" table...')
	db.exec('DROP TABLE users;') or {
		println('Drop table failed: ${err}')
	}
}

Chapter 13 Standard Library & Advanced Features

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:

Inline Assembly & C Interop

Networking (TCP, UDP, SSL, WebSockets)

Other Stdlib Updates

Strings.Lorem Helper

WebAssembly Compilation


This chapter highlights the power of V's standard library and advanced integration features, including low-level socket networking, inline assembly, compilation to WebAssembly, and V's unique memory management models.

Inline Assembly & C Interop

Inline Assembly

V supports inline assembly block definitions using the asm keyword, allowing developers to execute architecture-specific instructions directly from V code. It integrates directly with V variables by mapping them to inputs and outputs using register constraints.

This example demonstrates how to:

  1. Write inline assembly blocks targeted at specific architectures (e.g. arm64 and amd64).
  2. Utilize V's compile-time conditional block $if to compile architecture-specific assembly blocks.
  3. Map V variables to assembly inputs and outputs using semicolon constraint annotations (e.g. ; +r (res)).
v
Run
module main

// add_five adds 5 to the given integer using inline assembly.
// It uses compile-time conditional checks ($if) to select the correct
// assembly instructions depending on the target CPU architecture.
fn add_five(val int) int {
	mut res := val
	$if arm64 {
		// ARM64 inline assembly for Apple Silicon (macOS M-series) and ARM Linux/Android.
		// - Syntax: add destination, operand1, operand2
		// - Constraints: '; +r (res)' specifies that 'res' is both an input and output register.
		asm arm64 {
			add res, res, 5
			; +r (res)
		}
	} $else $if amd64 {
		// AMD64 (x86_64) inline assembly for Intel/AMD processors.
		// - Syntax: add destination, source
		// - Constraints: '; +r (res)' specifies that 'res' is both an input and output register.
		asm amd64 {
			add res, 5
			; +r (res)
		}
	} $else {
		// Fallback for other architectures (e.g. 32-bit x86, RISC-V, WebAssembly, etc.)
		res += 5
	}
	return res
}

// multiply_by_two multiplies the given integer by 2 using bit shifting in inline assembly.
fn multiply_by_two(val int) int {
	mut res := val
	$if arm64 {
		// ARM64 inline assembly using logical shift left (lsl) by 1 bit.
		asm arm64 {
			lsl res, res, 1
			; +r (res)
		}
	} $else $if amd64 {
		// AMD64 inline assembly using shift arithmetic left (sal).
		asm amd64 {
			sal res, 1
			; +r (res)
		}
	} $else {
		res = res << 1
	}
	return res
}

fn main() {
	println('=== V Inline Assembly (asm) Demo ===')

	// Test 1: Addition using inline assembly
	num := 10
	num_plus_five := add_five(num)
	println('Result of add_five(${num}): ${num_plus_five}')
	assert num_plus_five == 15

	// Test 2: Multiplication (bit-shifting) using inline assembly
	val := 21
	val_double := multiply_by_two(val)
	println('Result of multiply_by_two(${val}): ${val_double}')
	assert val_double == 42

	println('All inline assembly assertions successfully verified!')
}

Networking (TCP, UDP, SSL, WebSockets)

Net Urllib

This example demonstrates parsing URLs into components, escaping and unescaping query parameters, and encoding query parameters using the net.urllib module.

v
Run
module main

import net.urllib

fn main() {
	println('=== net.urllib Module Demo ===')

	// 1. Parsing a URL
	raw_url := 'https://user:pass@vlang.io:8080/docs/stdlib?lang=v&version=0.5.1#intro'
	println('Parsing URL: ${raw_url}')

	u := urllib.parse(raw_url) or {
		println('Failed to parse URL: ${err}')
		return
	}

	println('Parsed URL parts:')
	println('  Scheme:   ${u.scheme}')
	println('  Host:     ${u.host}')
	println('  Path:     ${u.path}')
	println('  Query:    ${u.raw_query}')
	println('  Fragment: ${u.fragment}')

	// 2. Query escaping and unescaping
	original_query := 'V compiler version 0.5.1 & details'
	escaped := urllib.query_escape(original_query)
	unescaped := urllib.query_unescape(escaped) or { 'failed' }

	println('\nQuery Escaping:')
	println('  Original:  ${original_query}')
	println('  Escaped:   ${escaped}')
	println('  Unescaped: ${unescaped}')

	// 3. Managing Query Parameters using urllib.Values
	println('\nManaging Query Values:')
	mut query_params := urllib.new_values()
	query_params.add('format', 'json')
	query_params.add('tags', 'programming')
	query_params.add('tags', 'tutorial')
	query_params.set('version', '0.5.1')

	// Encode to raw query string
	encoded_query := query_params.encode()
	println('  Encoded query string: ${encoded_query}')

	// Parse it back
	parsed_params := urllib.parse_query(encoded_query) or { urllib.new_values() }
	println('  Parsed format tag:    ${parsed_params.get('format') or { 'none' }}')
	println('  Parsed tags:          ${parsed_params.get_all('tags')}')
}

Net Websocket

This example demonstrates spinning up a local WebSocket server, connecting a WebSocket client to it, exchanging messages, and closing the connection cleanly using the net.websocket module.

v
Run
module main

import net.websocket
import time

fn main() {
	println('=== net.websocket Module Demo ===')

	port := 30099
	uri := 'ws://localhost:${port}'

	// 1. Initialize and run a local WebSocket server in a separate thread
	mut ws_server := websocket.new_server(.ip, port, '/')

	ws_server.on_connect(fn (mut s websocket.ServerClient) !bool {
		println('Server: Client connecting from ${s.client_key}')
		return true
	})!

	ws_server.on_message(fn (mut ws websocket.Client, msg &websocket.Message) ! {
		if msg.opcode == .text_frame {
			payload := msg.payload.bytestr()
			println('Server received text: "${payload}"')

			// Echo message back to client
			ws.write_string('Echo: ' + payload)!
		}
	})

	// Run the server listening loop in a background thread
	spawn fn [mut ws_server] () {
		ws_server.listen() or {
			println('Server error: ${err}')
		}
	}()

	// Allow the server a moment to start
	time.sleep(100 * time.millisecond)

	// 2. Initialize the WebSocket client
	mut ws_client := websocket.new_client(uri) or {
		println('Client init failed: ${err}')
		return
	}

	ws_client.on_open(fn (mut c websocket.Client) ! {
		println('Client: Connection opened!')
	})

	ws_client.on_message(fn (mut c websocket.Client, msg &websocket.Message) ! {
		if msg.opcode == .text_frame {
			payload := msg.payload.bytestr()
			println('Client received text response: "${payload}"')
		}
	})

	ws_client.on_error(fn (mut c websocket.Client, error_msg string) ! {
		println('Client error: ${error_msg}')
	})

	// 3. Connect and run the client
	ws_client.connect() or {
		println('Client failed to connect: ${err}')
		return
	}

	// Start the client listen loop in a background thread
	spawn ws_client.listen()

	// 4. Send a test message
	time.sleep(50 * time.millisecond)
	msg_to_send := 'Hello WebSocket Server!'
	println('Client sending: "${msg_to_send}"')
	ws_client.write_string(msg_to_send) or {
		println('Client failed to send: ${err}')
	}

	// Wait for echo to arrive
	time.sleep(200 * time.millisecond)

	// Clean close
	println('Client closing connection...')
	ws_client.close(1000, 'Done') or {
		println('Client close error: ${err}')
	}
	time.sleep(50 * time.millisecond)
	println('WebSocket Demo finished.')
}

Websocket Persistent

This example demonstrates a persistent WebSocket connection maintaining an active back-and-forth ping-pong conversation between client and server, terminating with a handshake exchange.

v
Run
module main

import net.websocket
import time

// ClientState maintains state across client callbacks
struct ClientState {
mut:
	count int
}

fn main() {
	println('=== Persistent WebSocket Demo ===')
	port := 38292
	uri := 'ws://localhost:${port}'

	// 1. Initialize and run a local WebSocket server
	mut ws_server := websocket.new_server(.ip, port, '/')

	ws_server.on_connect(fn (mut s websocket.ServerClient) !bool {
		println('Server: Client connected from ${s.client_key}')
		return true
	})!

	// Server message handler responds back to ping messages
	ws_server.on_message(fn (mut ws websocket.Client, msg &websocket.Message) ! {
		if msg.opcode == .text_frame {
			payload := msg.payload.bytestr()
			println('Server received text: "${payload}"')

			if payload.starts_with('Ping ') {
				num := payload.replace('Ping ', '')
				response := 'Pong ${num}'
				println('Server responding with: "${response}"')
				ws.write_string(response)!
			} else if payload == 'Goodbye' {
				println('Server received goodbye. Sending confirmation and closing...')
				ws.write_string('Goodbye!')!
			}
		}
	})

	// Start the server listen loop in a background thread
	spawn fn [mut ws_server] () {
		ws_server.listen() or {
			println('Server error: ${err}')
		}
	}()

	// Allow the server a moment to start
	time.sleep(100 * time.millisecond)

	// 2. Initialize the WebSocket client
	mut ws_client := websocket.new_client(uri) or {
		println('Client init failed: ${err}')
		return
	}

	mut state := &ClientState{
		count: 0
	}

	ws_client.on_open(fn (mut c websocket.Client) ! {
		println('Client: Connection opened!')
		// Initiate the first Ping message
		c.write_string('Ping 1')!
	})

	// Client message handler processes the Pong responses and decides
	// whether to send another Ping, or bid Goodbye.
	ws_client.on_message(fn [mut state] (mut c websocket.Client, msg &websocket.Message) ! {
		if msg.opcode == .text_frame {
			payload := msg.payload.bytestr()
			println('Client received response: "${payload}"')

			if payload.starts_with('Pong ') {
				state.count++
				if state.count < 3 {
					next_msg := 'Ping ${state.count + 1}'
					println('Client sending next message: "${next_msg}"')
					c.write_string(next_msg)!
				} else {
					println('Client sending goodbye: "Goodbye"')
					c.write_string('Goodbye')!
				}
			} else if payload == 'Goodbye!' {
				println('Client received goodbye response. Closing connection...')
				c.close(1000, 'Done') or {
					println('Client close error: ${err}')
				}
			}
		}
	})

	ws_client.on_close(fn (mut c websocket.Client, code int, reason string) ! {
		println('Client: Connection closed (code: ${code}, reason: "${reason}")')
	})

	ws_client.on_error(fn (mut c websocket.Client, error_msg string) ! {
		println('Client error: ${error_msg}')
	})

	// Connect and run the client listener
	ws_client.connect() or {
		println('Client failed to connect: ${err}')
		return
	}

	// Start the client listen loop in a background thread
	spawn ws_client.listen()

	// Wait for the conversational flow to finish
	time.sleep(1000 * time.millisecond)
	println('WebSocket Demo finished.')
}

Net Html

This example demonstrates parsing HTML strings, querying tags by class name and attribute values, and extracting node text and properties using the net.html module.

v
Run
module main

import net.html

fn main() {
	println('=== net.html Module Demo ===')

	// 1. Define a sample HTML document to parse
	html_content := '
	<!DOCTYPE html>
	<html>
	<head>
		<title>V Programming Language</title>
	</head>
	<body>
		<header>
			<h1 class="main-title">Welcome to the V Standard Library</h1>
		</header>
		<main>
			<div class="content" id="overview">
				<p class="description">
					V is a simple, fast, safe, and compiled language.
				</p>
				<p class="description">
					The net.html module parses HTML into a Queryable Document Object Model (DOM).
				</p>
				<a href="https://vlang.io" class="link-btn" id="home-link">Official Website</a>
				<a href="https://github.com/vlang/v" class="link-btn" id="repo-link">GitHub Repository</a>
			</div>
		</main>
	</body>
	</html>'

	// 2. Parse the HTML string into a DocumentObjectModel (DOM)
	println('Parsing HTML document...')
	dom := html.parse(html_content)

	// 3. Retrieve tags by name (e.g., header, title)
	title_tags := dom.get_tags(name: 'title')
	if title_tags.len > 0 {
		println('Page Title: "${title_tags[0].text()}"')
	}

	// 4. Retrieve tags by class name
	descriptions := dom.get_tags_by_class_name('description')
	println('\nParagraphs with class "description":')
	for i, p in descriptions {
		println('  ${i + 1}: ${p.text().trim_space()}')
	}

	// 5. Query tags by attribute value (e.g., href, id)
	links := dom.get_tags_by_class_name('link-btn')
	println('\nLinks found in document:')
	for link in links {
		href := link.attributes['href']
		id := link.attributes['id']
		text := link.text()
		println('  - Text: "${text}"')
		println('    ID:   "${id}"')
		println('    URL:  "${href}"')
	}

	// 6. Access DOM root and verify serialization representation
	root := dom.get_root()
	println('\nDOM Root Element Tag Name: <${root.name}>')
	println('HTML Demo finished.')
}

Net Jsonrpc

This example demonstrates implementing JSON-RPC 2.0 servers and clients using V's net.jsonrpc module, utilizing a Unix domain socket connection as the transport layer.

v
Run
module main

import net.unix
import net.jsonrpc
import os
import time

// Define structs for request parameters and response results
struct MathParams {
pub:
	a int
	b int
}

struct MathResult {
pub:
	sum        int
	difference int
}

// Router handler to compute mathematical operations
fn handle_math(req &jsonrpc.Request, mut wr jsonrpc.ResponseWriter) {
	// Decode request parameters into MathParams struct
	params := req.decode_params[MathParams]() or {
		wr.write_error(jsonrpc.invalid_params)
		return
	}

	result := MathResult{
		sum:        params.a + params.b
		difference: params.a - params.b
	}

	// Write the successful result back
	wr.write(result)
}

// Start JSON-RPC 2.0 Server over Unix Socket
fn run_rpc_server(socket_path string) ! {
	// Ensure cleanup of any old socket file
	if os.exists(socket_path) {
		os.rm(socket_path)!
	}

	mut listener := unix.listen_stream(socket_path, unix.ListenOptions{}) or {
		println('Server: Failed to listen: ${err}')
		return err
	}
	defer {
		listener.close() or {}
		listener.unlink() or {}
	}

	println('Server: Listening and waiting for connections...')

	// Accept client connection
	mut conn := listener.accept() or {
		println('Server: Accept failed: ${err}')
		return err
	}
	defer {
		conn.close() or {}
	}

	println('Server: Client connected, initiating JSON-RPC protocol.')

	// Setup JSON-RPC router and register math method
	mut router := jsonrpc.Router{}
	router.register('math.compute', handle_math)

	// Create JSON-RPC server wrapping the Unix socket connection stream
	mut server := jsonrpc.new_server(jsonrpc.ServerConfig{
		stream:  conn
		handler: router.handle_jsonrpc
	})

	// Process incoming request and respond
	server.respond() or {
		println('Server: Error processing request: ${err}')
		return err
	}
	println('Server: Successfully processed request and shut down.')
}

// Start JSON-RPC 2.0 Client over Unix Socket
fn run_rpc_client(socket_path string) ! {
	println('Client: Connecting to server at ${socket_path}...')
	mut conn := unix.connect_stream(socket_path) or {
		println('Client: Connection failed: ${err}')
		return err
	}
	defer {
		conn.close() or {}
	}

	// Create JSON-RPC client wrapping the Unix socket connection stream
	mut client := jsonrpc.new_client(jsonrpc.ClientConfig{
		stream: conn
	})

	params := MathParams{
		a: 45
		b: 17
	}

	println('Client: Sending request "math.compute" with params {a: ${params.a}, b: ${params.b}}')

	// Execute JSON-RPC request (method, parameters, request ID)
	resp := client.request('math.compute', params, 'req_math_1') or {
		println('Client: Request execution failed: ${err}')
		return err
	}

	// Decode response result
	result := resp.decode_result[MathResult]() or {
		println('Client: Failed to decode response result: ${err}')
		return err
	}

	println('Client received response:')
	println('  Request ID: ${resp.id}')
	println('  Result sum:        ${result.sum}')
	println('  Result difference: ${result.difference}')
}

fn main() {
	println('=== net.jsonrpc Module Demo ===')
	socket_path := os.join_path(os.temp_dir(), 'v_jsonrpc_example_socket')

	// Spawn JSON-RPC server in background thread
	spawn fn (path string) {
		run_rpc_server(path) or {
			println('Server thread failed: ${err}')
		}
	}(socket_path)

	// Wait briefly for the server socket to bind
	time.sleep(100 * time.millisecond)

	// Run JSON-RPC client in main thread
	run_rpc_client(socket_path) or {
		println('Client thread failed: ${err}')
	}

	// Wait briefly for server post-handling cleanups
	time.sleep(50 * time.millisecond)
	println('JSON-RPC Demo finished.')
}

Net Jsonrpc Persistent

This example demonstrates how to build a persistent JSON-RPC 2.0 connection. The server calls server.start() to continuously process incoming requests, and the client sends multiple method invocations sequentially over a single socket connection.

v
Run
module main

import net.unix
import net.jsonrpc
import os
import time

// Define structs for request parameters and response results
struct MathParams {
pub:
	a int
	b int
}

struct MathResult {
pub:
	sum        int
	difference int
}

// Router handler to compute mathematical operations
fn handle_math(req &jsonrpc.Request, mut wr jsonrpc.ResponseWriter) {
	params := req.decode_params[MathParams]() or {
		wr.write_error(jsonrpc.invalid_params)
		return
	}

	result := MathResult{
		sum:        params.a + params.b
		difference: params.a - params.b
	}

	wr.write(result)
}

// Start JSON-RPC 2.0 Server over Unix Socket in a persistent loop
fn run_rpc_server(socket_path string) ! {
	if os.exists(socket_path) {
		os.rm(socket_path)!
	}

	mut listener := unix.listen_stream(socket_path, unix.ListenOptions{}) or {
		println('Server: Failed to listen: ${err}')
		return err
	}
	defer {
		listener.close() or {}
		listener.unlink() or {}
	}

	println('Server: Listening and waiting for connections...')

	mut conn := listener.accept() or {
		println('Server: Accept failed: ${err}')
		return err
	}
	defer {
		conn.close() or {}
	}

	println('Server: Client connected, starting persistent JSON-RPC loop.')

	mut router := jsonrpc.Router{}
	router.register('math.compute', handle_math)

	mut server := jsonrpc.new_server(jsonrpc.ServerConfig{
		stream:  conn
		handler: router.handle_jsonrpc
	})

	// Start the server processing loop (calls s.respond() in a loop)
	server.start()
	println('Server: Loop finished.')
}

// Start JSON-RPC 2.0 Client and make multiple requests over the same connection
fn run_rpc_client(socket_path string) ! {
	println('Client: Connecting to server at ${socket_path}...')
	mut conn := unix.connect_stream(socket_path) or {
		println('Client: Connection failed: ${err}')
		return err
	}
	defer {
		conn.close() or {}
	}

	mut client := jsonrpc.new_client(jsonrpc.ClientConfig{
		stream: conn
	})

	// Perform multiple requests over the same persistent connection
	for i in 1 .. 4 {
		params := MathParams{
			a: 10 * i
			b: 5 * i
		}
		req_id := 'req_math_${i}'
		println('Client: Sending request "${req_id}" for math.compute with {a: ${params.a}, b: ${params.b}}')

		resp := client.request('math.compute', params, req_id) or {
			println('Client: Request failed: ${err}')
			return err
		}

		result := resp.decode_result[MathResult]() or {
			println('Client: Failed to decode result: ${err}')
			return err
		}

		println('Client received response for ${resp.id}:')
		println('  sum:        ${result.sum}')
		println('  difference: ${result.difference}')

		time.sleep(50 * time.millisecond)
	}
}

fn main() {
	println('=== Persistent net.jsonrpc Module Demo ===')
	socket_path := os.join_path(os.temp_dir(), 'v_jsonrpc_persistent_socket')

	// Spawn JSON-RPC server in background thread
	spawn fn (path string) {
		run_rpc_server(path) or {
			println('Server thread failed: ${err}')
		}
	}(socket_path)

	// Wait briefly for the server socket to bind
	time.sleep(100 * time.millisecond)

	// Run JSON-RPC client in main thread
	run_rpc_client(socket_path) or {
		println('Client thread failed: ${err}')
	}

	// Wait briefly for server post-handling cleanups
	time.sleep(50 * time.millisecond)
	println('Persistent JSON-RPC Demo finished.')
}

Net Ssl

This example demonstrates setting up a secure SSL/TLS server and client connection using the standard library's net.mbedtls module, including programmatically generating a self-signed key/cert pair using OpenSSL and cleaning them up on exit.

v
Run
module main

import net.mbedtls
import net
import os
import time

// generate_certs runs openssl to create a temporary self-signed certificate and key.
fn generate_certs() ! {
	println('Generating temporary self-signed SSL certificate...')
	res := os.execute('openssl req -x509 -newkey rsa:2048 -keyout temp_server.key -out temp_server.crt -days 1 -nodes -subj "/CN=localhost"')
	if res.exit_code != 0 {
		return error('Failed to generate certs: ${res.output}')
	}
}

// cleanup_certs deletes the temporary certificate and key files.
fn cleanup_certs() {
	println('Cleaning up temporary certificate files...')
	os.rm('temp_server.key') or {}
	os.rm('temp_server.crt') or {}
}

// run_server starts the SSL server, accepts a client connection,
// reads a message, responds securely, and exits.
fn run_server(port int) ! {
	config := mbedtls.SSLConnectConfig{
		cert: 'temp_server.crt'
		cert_key: 'temp_server.key'
		validate: false
	}

	mut listener := mbedtls.new_ssl_listener('127.0.0.1:${port}', config) or {
		println('Server: Failed to create listener: ${err}')
		return err
	}
	defer {
		listener.shutdown() or {}
	}

	println('Server: Listening on SSL port ${port}...')

	mut conn := listener.accept() or {
		println('Server: Failed to accept SSL connection: ${err}')
		return err
	}
	defer {
		conn.close() or {}
	}

	println('Server: SSL Client connected!')

	mut buf := []u8{len: 1024}
	n := conn.read(mut buf) or {
		println('Server: Read failed: ${err}')
		return err
	}

	message := buf[..n].bytestr()
	println('Server: Received message: "${message}"')

	// Respond securely
	response := 'Echo Secure: ${message}'
	conn.write(response.bytes()) or {
		println('Server: Write failed: ${err}')
		return err
	}
	println('Server: Sent secure response.')
}

// run_client connects to the server port via TCP first, wraps it in SSL,
// sends a message, reads the secure response, and closes.
fn run_client(port int) ! {
	println('Client: Dialing standard TCP port first...')
	mut tcp_conn := net.dial_tcp('127.0.0.1:${port}') or {
		println('Client: Failed to connect standard TCP: ${err}')
		return err
	}

	println('Client: Initiating SSL handshake on top of TCP connection...')
	config := mbedtls.SSLConnectConfig{
		validate: false
	}

	mut ssl_conn := mbedtls.new_ssl_conn(config) or {
		println('Client: Failed to create SSL connection struct: ${err}')
		return err
	}
	defer {
		ssl_conn.close() or {}
	}

	ssl_conn.connect(mut tcp_conn, 'localhost') or {
		println('Client: SSL handshake failed: ${err}')
		return err
	}

	println('Client: Secure connection established!')

	message := 'Hello V Secure Sockets!'
	println('Client: Sending message: "${message}"')
	ssl_conn.write(message.bytes()) or {
		println('Client: Write failed: ${err}')
		return err
	}

	mut buf := []u8{len: 1024}
	n := ssl_conn.read(mut buf) or {
		println('Client: Read failed: ${err}')
		return err
	}

	response := buf[..n].bytestr()
	println('Client: Received response: "${response}"')
}

fn main() {
	println('=== net.ssl Module Demo ===')
	generate_certs() or {
		println('Error generating certs: ${err}')
		return
	}
	defer {
		cleanup_certs()
	}

	port := 38295
	// Spawn server in background
	spawn fn (p int) {
		run_server(p) or {
			println('Server thread failed: ${err}')
		}
	}(port)

	// Wait briefly for server to bind
	time.sleep(200 * time.millisecond)

	// Run client in main thread
	run_client(port) or {
		println('Client failed: ${err}')
	}

	time.sleep(50 * time.millisecond)
	println('SSL Demo finished.')
}

Ssl Persistent

This example demonstrates keeping an SSL/TLS connection open for multiple rounds of secure back-and-forth ping-pong communication over net.mbedtls.

v
Run
module main

import net.mbedtls
import net
import os
import time

// generate_certs runs openssl to create a temporary self-signed certificate and key.
fn generate_certs() ! {
	println('Generating temporary self-signed SSL certificate...')
	res := os.execute('openssl req -x509 -newkey rsa:2048 -keyout temp_server.key -out temp_server.crt -days 1 -nodes -subj "/CN=localhost"')
	if res.exit_code != 0 {
		return error('Failed to generate certs: ${res.output}')
	}
}

// cleanup_certs deletes the temporary certificate and key files.
fn cleanup_certs() {
	println('Cleaning up temporary certificate files...')
	os.rm('temp_server.key') or {}
	os.rm('temp_server.crt') or {}
}

// run_server starts the SSL server, accepts a client connection,
// and processes incoming messages in a loop until the client sends "Goodbye".
fn run_server(port int) ! {
	config := mbedtls.SSLConnectConfig{
		cert: 'temp_server.crt'
		cert_key: 'temp_server.key'
		validate: false
	}

	mut listener := mbedtls.new_ssl_listener('127.0.0.1:${port}', config) or {
		println('Server: Failed to create listener: ${err}')
		return err
	}
	defer {
		listener.shutdown() or {}
	}

	println('Server: Listening on SSL port ${port}...')

	mut conn := listener.accept() or {
		println('Server: Failed to accept SSL connection: ${err}')
		return err
	}
	defer {
		conn.close() or {}
	}

	println('Server: SSL Client connected!')

	// Loop to handle back-and-forth messages on the same secure connection
	for {
		mut buf := []u8{len: 1024}
		n := conn.read(mut buf) or {
			println('Server: Secure connection closed or read error: ${err}')
			break
		}
		if n == 0 {
			println('Server: Client disconnected.')
			break
		}

		message := buf[..n].bytestr()
		println('Server received secure: "${message}"')

		if message == 'Goodbye' {
			println('Server received Goodbye. Replying and closing secure connection...')
			conn.write('Goodbye!'.bytes()) or {
				println('Server: Write failed: ${err}')
			}
			break
		}

		response := 'Echo: ${message}'
		println('Server sending secure: "${response}"')
		conn.write(response.bytes()) or {
			println('Server: Write failed: ${err}')
			break
		}
	}
	println('Server finished.')
}

// run_client connects to the server port via TCP first, wraps it in SSL,
// and sends multiple messages in a loop before ending the secure session cleanly.
fn run_client(port int) ! {
	println('Client: Dialing standard TCP port first...')
	mut tcp_conn := net.dial_tcp('127.0.0.1:${port}') or {
		println('Client: Failed to connect standard TCP: ${err}')
		return err
	}

	println('Client: Initiating SSL handshake on top of TCP connection...')
	config := mbedtls.SSLConnectConfig{
		validate: false
	}

	mut ssl_conn := mbedtls.new_ssl_conn(config) or {
		println('Client: Failed to create SSL connection struct: ${err}')
		return err
	}
	defer {
		ssl_conn.close() or {}
	}

	ssl_conn.connect(mut tcp_conn, 'localhost') or {
		println('Client: SSL handshake failed: ${err}')
		return err
	}

	println('Client: Secure connection established!')

	// Exchange multiple messages
	for i in 1 .. 4 {
		message := 'Ping ${i}'
		println('Client sending secure: "${message}"')
		ssl_conn.write(message.bytes()) or {
			println('Client: Write failed: ${err}')
			return err
		}

		// Read response
		mut buf := []u8{len: 1024}
		n := ssl_conn.read(mut buf) or {
			println('Client: Read failed: ${err}')
			return err
		}
		if n == 0 {
			println('Client: Server closed secure connection.')
			return error('Server closed connection unexpectedly')
		}

		response := buf[..n].bytestr()
		println('Client received secure response: "${response}"')

		time.sleep(50 * time.millisecond)
	}

	// Send Goodbye to cleanly terminate the persistent session
	println('Client sending: "Goodbye"')
	ssl_conn.write('Goodbye'.bytes()) or {
		println('Client: Write failed: ${err}')
		return err
	}

	mut buf := []u8{len: 1024}
	n := ssl_conn.read(mut buf) or {
		println('Client: Read failed: ${err}')
		return err
	}
	if n > 0 {
		response := buf[..n].bytestr()
		println('Client received secure response: "${response}"')
	}
	println('Client finished.')
}

fn main() {
	println('=== Persistent SSL Demo ===')
	generate_certs() or {
		println('Error generating certs: ${err}')
		return
	}
	defer {
		cleanup_certs()
	}

	port := 38296
	// Spawn the server in a background thread
	spawn fn (p int) {
		run_server(p) or {
			println('Server thread failed: ${err}')
		}
	}(port)

	// Allow the server thread a short time to start and bind
	time.sleep(200 * time.millisecond)

	// Run the client in the main thread
	run_client(port) or {
		println('Client failed: ${err}')
	}

	// Give the server a small window to finish deferred cleanups
	time.sleep(50 * time.millisecond)
	println('SSL Sockets Demo finished.')
}

Net Tcp

This example demonstrates how to create a simple TCP server and client in V. The server listens on a local port, accepts an incoming client connection, receives data, sends a response, and closes the connection.

v
Run
module main

import net
import time

// run_server starts the TCP server on the specified port, accepts a connection,
// reads a message, responds, and closes the connection.
fn run_server(port int) ! {
	mut listener := net.listen_tcp(.ip, '127.0.0.1:${port}') or {
		println('Server: Failed to listen on port ${port}: ${err}')
		return err
	}
	defer {
		listener.close() or {}
	}

	println('Server: Listening on 127.0.0.1:${port}...')

	mut conn := listener.accept() or {
		println('Server: Failed to accept connection: ${err}')
		return err
	}
	defer {
		conn.close() or {}
	}

	println('Server: Client connected!')

	mut buf := []u8{len: 1024}
	n := conn.read(mut buf) or {
		println('Server: Read failed: ${err}')
		return err
	}

	message := buf[..n].bytestr()
	println('Server: Received message: "${message}"')

	// Write response back to the client
	response := 'Echo: ${message}'
	conn.write(response.bytes()) or {
		println('Server: Write failed: ${err}')
		return err
	}
	println('Server: Sent echo response.')
}

// run_client connects to the TCP server, sends a message, reads the response,
// and closes the connection.
fn run_client(port int) ! {
	println('Client: Connecting to 127.0.0.1:${port}...')
	mut conn := net.dial_tcp('127.0.0.1:${port}') or {
		println('Client: Failed to connect: ${err}')
		return err
	}
	defer {
		conn.close() or {}
	}

	println('Client: Connected!')

	// Send message to the server
	message := 'Hello V TCP Sockets!'
	println('Client: Sending message: "${message}"')
	conn.write(message.bytes()) or {
		println('Client: Write failed: ${err}')
		return err
	}

	// Read server response
	mut buf := []u8{len: 1024}
	n := conn.read(mut buf) or {
		println('Client: Read failed: ${err}')
		return err
	}

	response := buf[..n].bytestr()
	println('Client: Received response: "${response}"')
}

fn main() {
	println('=== net.tcp Module Demo ===')
	port := 38290

	// Spawn the server in a background thread
	spawn fn (p int) {
		run_server(p) or {
			println('Server thread failed: ${err}')
		}
	}(port)

	// Allow the server thread a short time to start and bind
	time.sleep(100 * time.millisecond)

	// Run the client in the main thread
	run_client(port) or {
		println('Client failed: ${err}')
	}

	// Give the server a small window to finish deferred cleanups
	time.sleep(50 * time.millisecond)
	println('TCP Demo finished.')
}

Tcp Persistent

This example demonstrates a persistent TCP connection. The client connects to the server, and they exchange multiple messages back-and-forth in a loop before closing the connection cleanly.

v
Run
module main

import net
import time

// run_server starts the TCP server on the specified port, accepts a connection,
// and processes incoming messages in a loop until the client sends "Goodbye".
fn run_server(port int) ! {
	mut listener := net.listen_tcp(.ip, '127.0.0.1:${port}') or {
		println('Server: Failed to listen on port ${port}: ${err}')
		return err
	}
	defer {
		listener.close() or {}
	}

	println('Server: Listening on 127.0.0.1:${port}...')

	mut conn := listener.accept() or {
		println('Server: Failed to accept connection: ${err}')
		return err
	}
	defer {
		conn.close() or {}
	}

	println('Server: Client connected!')

	// Loop to handle back-and-forth messages on the same connection
	for {
		mut buf := []u8{len: 1024}
		n := conn.read(mut buf) or {
			println('Server: Connection closed or read error: ${err}')
			break
		}
		if n == 0 {
			println('Server: Client disconnected.')
			break
		}

		message := buf[..n].bytestr()
		println('Server received: "${message}"')

		if message == 'Goodbye' {
			println('Server received Goodbye. Replying and closing connection...')
			conn.write('Goodbye!'.bytes()) or {
				println('Server: Write failed: ${err}')
			}
			break
		}

		response := 'Echo: ${message}'
		println('Server sending: "${response}"')
		conn.write(response.bytes()) or {
			println('Server: Write failed: ${err}')
			break
		}
	}
	println('Server finished.')
}

// run_client connects to the TCP server, sends multiple messages,
// receives replies, and finally sends a goodbye message.
fn run_client(port int) ! {
	println('Client: Connecting to 127.0.0.1:${port}...')
	mut conn := net.dial_tcp('127.0.0.1:${port}') or {
		println('Client: Failed to connect: ${err}')
		return err
	}
	defer {
		conn.close() or {}
	}

	println('Client: Connected!')

	// Exchange multiple messages
	for i in 1 .. 4 {
		message := 'Ping ${i}'
		println('Client sending: "${message}"')
		conn.write(message.bytes()) or {
			println('Client: Write failed: ${err}')
			return err
		}

		// Read response
		mut buf := []u8{len: 1024}
		n := conn.read(mut buf) or {
			println('Client: Read failed: ${err}')
			return err
		}
		if n == 0 {
			println('Client: Server closed connection.')
			return error('Server closed connection unexpectedly')
		}

		response := buf[..n].bytestr()
		println('Client received response: "${response}"')

		time.sleep(50 * time.millisecond)
	}

	// Send Goodbye to cleanly terminate the persistent session
	println('Client sending: "Goodbye"')
	conn.write('Goodbye'.bytes()) or {
		println('Client: Write failed: ${err}')
		return err
	}

	mut buf := []u8{len: 1024}
	n := conn.read(mut buf) or {
		println('Client: Read failed: ${err}')
		return err
	}
	if n > 0 {
		response := buf[..n].bytestr()
		println('Client received response: "${response}"')
	}
	println('Client finished.')
}

fn main() {
	println('=== Persistent TCP Demo ===')
	port := 38293

	// Spawn the server in a background thread
	spawn fn (p int) {
		run_server(p) or {
			println('Server thread failed: ${err}')
		}
	}(port)

	// Allow the server thread a short time to start and bind
	time.sleep(100 * time.millisecond)

	// Run the client in the main thread
	run_client(port) or {
		println('Client failed: ${err}')
	}

	// Give the server a small window to finish deferred cleanups
	time.sleep(50 * time.millisecond)
	println('TCP Sockets Demo finished.')
}

Net Udp

This example demonstrates sending and receiving connectionless UDP packets. The server binds to a local port and receives a message along with the sender's address, and responds to it using write_to.

v
Run
module main

import net
import time

// run_server starts the UDP server on the specified port, listens for a packet,
// prints the message, sends a response back to the sender, and exits.
fn run_server(port int) ! {
	mut socket := net.listen_udp('127.0.0.1:${port}') or {
		println('Server: Failed to listen on port ${port}: ${err}')
		return err
	}
	defer {
		socket.close() or {}
	}

	println('Server: Listening for UDP packets on port ${port}...')

	mut buf := []u8{len: 1024}
	read, addr := socket.read(mut buf) or {
		println('Server: Read failed: ${err}')
		return err
	}

	message := buf[..read].bytestr()
	println('Server: Received message from ${addr}: "${message}"')

	// Send echo response
	response := 'Echo: ${message}'
	socket.write_to(addr, response.bytes()) or {
		println('Server: Send failed: ${err}')
		return err
	}
	println('Server: Sent echo response.')
}

// run_client creates a UDP client socket, sends a datagram, and waits for a response.
fn run_client(port int) ! {
	mut socket := net.dial_udp('127.0.0.1:${port}') or {
		println('Client: Failed to dial server: ${err}')
		return err
	}
	defer {
		socket.close() or {}
	}

	message := 'Hello V UDP Sockets!'
	println('Client: Sending message: "${message}"')
	socket.write(message.bytes()) or {
		println('Client: Write failed: ${err}')
		return err
	}

	// Read server response
	mut buf := []u8{len: 1024}
	read, addr := socket.read(mut buf) or {
		println('Client: Read failed: ${err}')
		return err
	}

	response := buf[..read].bytestr()
	println('Client: Received response from ${addr}: "${response}"')
}

fn main() {
	println('=== net.udp Module Demo ===')
	port := 38291

	// Spawn the server in a background thread
	spawn fn (p int) {
		run_server(p) or {
			println('Server thread failed: ${err}')
		}
	}(port)

	// Allow the server thread a short time to start and bind
	time.sleep(100 * time.millisecond)

	// Run the client in the main thread
	run_client(port) or {
		println('Client failed: ${err}')
	}

	// Give the server a small window to finish deferred cleanups
	time.sleep(50 * time.millisecond)
	println('UDP Demo finished.')
}

Udp Persistent

This example demonstrates how to maintain a persistent ping-pong message exchange over UDP. The client continuously sends datagrams using a bound socket, and the server receives them in a loop while retaining the sender's details to write back.

v
Run
module main

import net
import time

// run_server starts the UDP server on the specified port, listens for packets,
// prints incoming messages and their source addresses, and responds to each
// in a loop until the client sends "Goodbye".
fn run_server(port int) ! {
	mut socket := net.listen_udp('127.0.0.1:${port}') or {
		println('Server: Failed to listen on port ${port}: ${err}')
		return err
	}
	defer {
		socket.close() or {}
	}

	println('Server: Listening for UDP packets on port ${port}...')

	// Loop to handle incoming UDP datagrams continuously
	for {
		mut buf := []u8{len: 1024}
		read, addr := socket.read(mut buf) or {
			println('Server: Read failed: ${err}')
			break
		}
		if read == 0 {
			break
		}

		message := buf[..read].bytestr()
		println('Server received from ${addr}: "${message}"')

		if message == 'Goodbye' {
			println('Server received Goodbye. Replying and exiting...')
			socket.write_to(addr, 'Goodbye!'.bytes()) or {
				println('Server: Write failed: ${err}')
			}
			break
		}

		response := 'Echo: ${message}'
		println('Server sending to ${addr}: "${response}"')
		socket.write_to(addr, response.bytes()) or {
			println('Server: Write failed: ${err}')
			break
		}
	}
	println('Server finished.')
}

// run_client creates a UDP socket bound to a remote destination address,
// and sends multiple messages in sequence, receiving responses from the server.
fn run_client(port int) ! {
	mut socket := net.dial_udp('127.0.0.1:${port}') or {
		println('Client: Failed to dial server: ${err}')
		return err
	}
	defer {
		socket.close() or {}
	}

	// Exchange multiple messages
	for i in 1 .. 4 {
		message := 'Ping ${i}'
		println('Client sending: "${message}"')
		socket.write(message.bytes()) or {
			println('Client: Write failed: ${err}')
			return err
		}

		// Read response
		mut buf := []u8{len: 1024}
		read, addr := socket.read(mut buf) or {
			println('Client: Read failed: ${err}')
			return err
		}

		response := buf[..read].bytestr()
		println('Client received response from ${addr}: "${response}"')

		time.sleep(50 * time.millisecond)
	}

	// Send Goodbye to cleanly terminate the session
	println('Client sending: "Goodbye"')
	socket.write('Goodbye'.bytes()) or {
		println('Client: Write failed: ${err}')
		return err
	}

	mut buf := []u8{len: 1024}
	read, addr := socket.read(mut buf) or {
		println('Client: Read failed: ${err}')
		return err
	}
	if read > 0 {
		response := buf[..read].bytestr()
		println('Client received response from ${addr}: "${response}"')
	}
	println('Client finished.')
}

fn main() {
	println('=== Persistent UDP Demo ===')
	port := 38294

	// Spawn the server in a background thread
	spawn fn (p int) {
		run_server(p) or {
			println('Server thread failed: ${err}')
		}
	}(port)

	// Allow the server thread a short time to start and bind
	time.sleep(100 * time.millisecond)

	// Run the client in the main thread
	run_client(port) or {
		println('Client failed: ${err}')
	}

	// Give the server a small window to finish deferred cleanups
	time.sleep(50 * time.millisecond)
	println('UDP Sockets Demo finished.')
}

Net Unix

This example demonstrates Unix domain socket client-server communication using the net.unix module.

v
Run
module main

import net.unix
import os
import time

// run_server starts the Unix socket server, accepts one client connection,
// echoes back the received message, and exits.
fn run_server(socket_path string) ! {
	// Clean up any stale socket file from a previous run
	if os.exists(socket_path) {
		os.rm(socket_path)!
	}

	// Listen on the Unix socket path with default options
	mut listener := unix.listen_stream(socket_path, unix.ListenOptions{}) or {
		println('Server: Failed to listen on ${socket_path}: ${err}')
		return err
	}
	defer {
		listener.close() or {}
		listener.unlink() or {}
	}

	println('Server: Listening on socket path: ${socket_path}')

	// Accept an incoming connection
	mut conn := listener.accept() or {
		println('Server: Failed to accept connection: ${err}')
		return err
	}
	defer {
		conn.close() or {}
	}

	println('Server: Client connected!')

	// Read client's message
	mut buf := []u8{len: 1024}
	n := conn.read(mut buf) or {
		println('Server: Read failed: ${err}')
		return err
	}

	message := buf[..n].bytestr()
	println('Server: Received message: "${message}"')

	// Write response back to the client
	response := 'Echo: ${message}'
	conn.write(response.bytes()) or {
		println('Server: Write failed: ${err}')
		return err
	}
	println('Server: Sent echo response.')
}

// run_client connects to the Unix socket server, sends a message,
// reads the echo response, and closes the connection.
fn run_client(socket_path string) ! {
	println('Client: Connecting to ${socket_path}...')
	mut conn := unix.connect_stream(socket_path) or {
		println('Client: Failed to connect: ${err}')
		return err
	}
	defer {
		conn.close() or {}
	}

	println('Client: Connected!')

	// Send message to the server
	message := 'Hello V Unix Domain Sockets!'
	println('Client: Sending message: "${message}"')
	conn.write(message.bytes()) or {
		println('Client: Write failed: ${err}')
		return err
	}

	// Read server response
	mut buf := []u8{len: 1024}
	n := conn.read(mut buf) or {
		println('Client: Read failed: ${err}')
		return err
	}

	response := buf[..n].bytestr()
	println('Client: Received response: "${response}"')
}

fn main() {
	println('=== net.unix Module Demo ===')

	// Create a unique temporary socket path
	socket_path := os.join_path(os.temp_dir(), 'v_unix_socket_example')

	// Spawn the server in a background thread
	spawn fn (path string) {
		run_server(path) or {
			println('Server thread failed: ${err}')
		}
	}(socket_path)

	// Allow the server thread a short time to start and bind
	time.sleep(100 * time.millisecond)

	// Run the client in the main thread
	run_client(socket_path) or {
		println('Client failed: ${err}')
	}

	// Give the server a small window to finish deferred cleanups
	time.sleep(50 * time.millisecond)
	println('Unix Sockets Demo finished.')
}

Unix Persistent

This example demonstrates establishing a Unix domain socket server and client, keeping the connection open for multiple rounds of back-and-forth communication, and terminating cleanly.

v
Run
module main

import net.unix
import os
import time

// run_server starts the Unix socket server, accepts one client connection,
// and processes incoming messages in a loop until the client says "Goodbye".
fn run_server(socket_path string) ! {
	// Clean up any stale socket file from a previous run
	if os.exists(socket_path) {
		os.rm(socket_path)!
	}

	// Listen on the Unix socket path
	mut listener := unix.listen_stream(socket_path, unix.ListenOptions{}) or {
		println('Server: Failed to listen on ${socket_path}: ${err}')
		return err
	}
	defer {
		listener.close() or {}
		listener.unlink() or {}
	}

	println('Server: Listening on socket path: ${socket_path}')

	// Accept a connection
	mut conn := listener.accept() or {
		println('Server: Failed to accept connection: ${err}')
		return err
	}
	defer {
		conn.close() or {}
	}

	println('Server: Client connected!')

	// Loop to handle back-and-forth messages on the same connection
	for {
		mut buf := []u8{len: 1024}
		n := conn.read(mut buf) or {
			println('Server: Connection closed or read error: ${err}')
			break
		}
		if n == 0 {
			println('Server: Client disconnected.')
			break
		}

		message := buf[..n].bytestr()
		println('Server received: "${message}"')

		if message == 'Goodbye' {
			println('Server received Goodbye. Replying and shutting down connection...')
			conn.write('Goodbye!'.bytes()) or {
				println('Server: Write failed: ${err}')
			}
			break
		}

		response := 'Echo: ${message}'
		println('Server sending: "${response}"')
		conn.write(response.bytes()) or {
			println('Server: Write failed: ${err}')
			break
		}
	}
	println('Server finished.')
}

// run_client connects to the Unix socket server, sends multiple messages,
// receives replies, and finally sends a goodbye message.
fn run_client(socket_path string) ! {
	println('Client: Connecting to ${socket_path}...')
	mut conn := unix.connect_stream(socket_path) or {
		println('Client: Failed to connect: ${err}')
		return err
	}
	defer {
		conn.close() or {}
	}

	println('Client: Connected!')

	// Exchange multiple messages
	for i in 1 .. 4 {
		message := 'Ping ${i}'
		println('Client sending: "${message}"')
		conn.write(message.bytes()) or {
			println('Client: Write failed: ${err}')
			return err
		}

		// Read response
		mut buf := []u8{len: 1024}
		n := conn.read(mut buf) or {
			println('Client: Read failed: ${err}')
			return err
		}
		if n == 0 {
			println('Client: Server closed connection.')
			return error('Server closed connection unexpectedly')
		}

		response := buf[..n].bytestr()
		println('Client received response: "${response}"')

		time.sleep(50 * time.millisecond)
	}

	// Send Goodbye to cleanly terminate the persistent session
	println('Client sending: "Goodbye"')
	conn.write('Goodbye'.bytes()) or {
		println('Client: Write failed: ${err}')
		return err
	}

	mut buf := []u8{len: 1024}
	n := conn.read(mut buf) or {
		println('Client: Read failed: ${err}')
		return err
	}
	if n > 0 {
		response := buf[..n].bytestr()
		println('Client received response: "${response}"')
	}
	println('Client finished.')
}

fn main() {
	println('=== Persistent Unix Sockets Demo ===')
	socket_path := os.join_path(os.temp_dir(), 'v_unix_socket_persistent')

	// Spawn the server in a background thread
	spawn fn (path string) {
		run_server(path) or {
			println('Server thread failed: ${err}')
		}
	}(socket_path)

	// Allow the server thread a short time to start and bind
	time.sleep(100 * time.millisecond)

	// Run the client in the main thread
	run_client(socket_path) or {
		println('Client failed: ${err}')
	}

	// Give the server a small window to finish deferred cleanups
	time.sleep(50 * time.millisecond)
	println('Unix Sockets Demo finished.')
}

Other Stdlib Updates

This section is grouped into focused subtopics so you can jump quickly to the area you need.

Core Language and Type Features

Standard Library and OS Modules

Security, Data, and Formats

Concurrency, CLI, and App Development

I/O, Streams, and Terminal

Options And Results

Options And Results

V has a very rich and growing standard library and is actively updated. This lesson on Options And Results showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of options and results.

v
Run
module main

// Result type (!T) is used when a function can return an error.
fn divide(a f64, b f64) !f64 {
	if b == 0 {
		return error('division by zero')
	}
	return a / b
}

// Option type (?T) is used when a function can return nothing (none).
fn find_user(id int) ?string {
	if id == 1 {
		return 'Alice'
	}
	return none
}

fn main() {
	// 1. Handling a Result type with an `or` block
	// Inside the `or` block, the special variable `err` is available.
	val1 := divide(10.0, 2.0) or {
		println('Error: ${err}')
		0.0
	}
	println('Result 1: ${val1}')

	// 2. Handling a failed Result
	val2 := divide(10.0, 0.0) or {
		println('Error: ${err}')
		0.0
	}
	println('Result 2: ${val2}')

	// 3. Handling an Option type with an `or` block
	// For Option types, the value is unwrapped or the fallback value is returned.
	user1 := find_user(1) or { 'Guest' }
	println('User 1: ${user1}')

	user2 := find_user(99) or { 'Guest' }
	println('User 2: ${user2}')

	// 4. Using if-let syntax to check Options
	if name := find_user(1) {
		println('Found user: ${name}')
	} else {
		println('User not found')
	}
}

Generics

Generics

V has a very rich and growing standard library and is actively updated. This lesson on Generics showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of generics.

v
Run
module main

// Stack[T] represents a generic stack structure.
struct Stack[T] {
mut:
	items []T
}

// push appends an item of type T to the stack.
fn (mut s Stack[T]) push(item T) {
	s.items << item
}

// pop removes and returns the top item of type T from the stack,
// or returns `none` (Option type) if the stack is empty.
fn (mut s Stack[T]) pop() ?T {
	if s.items.len == 0 {
		return none
	}
	return s.items.pop()
}

// print_val is a generic function that takes any type T and prints it.
fn print_val[T](val T) {
	println('Value: ${val}')
}

fn main() {
	// 1. Using a generic struct with integers
	mut int_stack := Stack[int]{}
	int_stack.push(10)
	int_stack.push(20)
	println('Popped: ${int_stack.pop() or { 0 }}')
	println('Popped: ${int_stack.pop() or { 0 }}')
	println('Popped from empty stack: ${int_stack.pop() or { -1 }}')

	// 2. Using the same generic struct with strings
	mut str_stack := Stack[string]{}
	str_stack.push('V')
	str_stack.push('lang')
	println('Popped: ${str_stack.pop() or { 'empty' }}')
	println('Popped: ${str_stack.pop() or { 'empty' }}')

	// 3. Calling a generic function with different types
	print_val[string]('V monomorphizes generics at compile-time!')
	print_val[f64](3.14159)
}

Interfaces

Interfaces

V has a very rich and growing standard library and is actively updated. This lesson on Interfaces showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of interfaces.

v
Run
module main

// Speaker is an interface. Any struct that implements a `speak() string` method
// implicitly implements Speaker. There is no `implements` keyword.
interface Speaker {
	speak() string
}

struct Dog {
	name string
}

// speak implements Speaker for Dog
fn (d Dog) speak() string {
	return 'Woof! My name is ${d.name}.'
}

struct Cat {
	name string
}

// speak implements Speaker for Cat
fn (c Cat) speak() string {
	return 'Meow! My name is ${c.name}.'
}

// perform_speak accepts any type implementing the Speaker interface
fn perform_speak(s Speaker) {
	println(s.speak())
}

fn main() {
	d := Dog{
		name: 'Buddy'
	}
	c := Cat{
		name: 'Whiskers'
	}

	// 1. Passing structs directly to functions expecting an interface
	perform_speak(d)
	perform_speak(c)

	// 2. Creating an array of interfaces
	speakers := [Speaker(d), Speaker(c)]
	for speaker in speakers {
		println('From array: ${speaker.speak()}')
	}
}

Sum Types

Sum Types

V has a very rich and growing standard library and is actively updated. This lesson on Sum Types showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of sum types.

v
Run
module main

// Define structs for different shapes
struct Circle {
	radius f64
}

struct Rectangle {
	width  f64
	height f64
}

struct Triangle {
	base   f64
	height f64
}

// Shape is a Sum Type. A Shape variable can store a Circle, Rectangle, or Triangle.
type Shape = Circle | Rectangle | Triangle

// get_area calculates the area depending on the concrete type stored in Shape.
fn get_area(s Shape) f64 {
	// Inside the match branches, the variable is smart-casted to its concrete type.
	match s {
		Circle {
			return 3.14159 * s.radius * s.radius
		}
		Rectangle {
			return s.width * s.height
		}
		Triangle {
			return 0.5 * s.base * s.height
		}
	}
}

fn main() {
	// 1. Creating values of the sum type
	shapes := [
		Shape(Circle{
			radius: 5.0
		}),
		Shape(Rectangle{
			width:  4.0
			height: 6.0
		}),
		Shape(Triangle{
			base:   3.0
			height: 4.0
		}),
	]

	// 2. Iterating and pattern-matching
	for shape in shapes {
		match shape {
			Circle {
				println('Found Circle with radius ${shape.radius}. Area: ${get_area(shape):.2f}')
			}
			Rectangle {
				println('Found Rectangle of ${shape.width}x${shape.height}. Area: ${get_area(shape):.2f}')
			}
			Triangle {
				println('Found Triangle with base ${shape.base} and height ${shape.height}. Area: ${get_area(shape):.2f}')
			}
		}
	}
}

Attributes

Attributes

V has a very rich and growing standard library and is actively updated. This lesson on Attributes showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of attributes.

v
Run
module main

import json

// User struct defines field attributes for custom JSON serialization/deserialization.
struct User {
	name string @[json: 'username']
	age  int    @[json: 'user_age']
}

// @[deprecated] warns the developer at compile time that the function shouldn't be used.
@[deprecated: 'use modern_greet instead']
fn old_greet() {
	println('Hello from the old greeting!')
}

fn modern_greet() {
	println('Hello from the modern greeting!')
}

// @[inline] suggests the compiler to inline the function body.
@[inline]
fn add(a int, b int) int {
	return a + b
}

fn main() {
	// 1. JSON serialization/deserialization using @[json] mapping
	u := User{
		name: 'Bob'
		age:  30
	}
	encoded := json.encode(u)
	println('Encoded JSON: ${encoded}')

	decoded := json.decode(User, '{"username":"Alice","user_age":25}') or {
		println('JSON error: ${err}')
		User{}
	}
	println('Decoded User -> Name: ${decoded.name}, Age: ${decoded.age}')

	// 2. Calling inline function
	sum := add(10, 20)
	println('Sum: ${sum}')

	// 3. Calling modern function
	modern_greet()

	// Note: Calling old_greet() will compile successfully but output a warning:
	// warning: old_greet has been deprecated. use modern_greet instead
	// old_greet()
}

Compile-Time Directives

Compile-Time Directives

V provides compile-time directives (starting with $) that are processed by the compiler before compilation. These directives allow you to perform conditional compilation based on OS or flags, retrieve environment variables, embed files directly into the compiled binary, and compile templates at compile time.

Additional Context from Repository docs:

This example demonstrates the concepts of compile-time directives.

v
Run
module main

fn main() {
	println('=== V Compile-Time Directives Demo ===')

	// 1. $if Directive (Conditional Compilation)
	println('\n--- 1. Conditional Compilation (\$if) ---')
	$if macos {
		println('Compiled specifically for macOS.')
	}
	$if windows {
		println('Compiled specifically for Windows.')
	}
	$if linux {
		println('Compiled specifically for Linux.')
	}

	$if debug {
		println('Debug mode is active.')
	} $else {
		println('Running in standard release/dev mode.')
	}

	// 2. $env Directive (Compile-time Environment Variables)
	println('\n--- 2. Compile-Time Environment (\$env) ---')
	// Retrieves the value of the environment variable at compilation time
	compile_path := $env('PATH')
	println('PATH length at compile-time: ${compile_path.len} bytes')

	// 3. $embed_file Directive (Compile-time Asset Embedding)
	println('\n--- 3. Asset Embedding (\$embed_file) ---')
	// Embeds the file content directly into the binary at compile time.
	// Returns an embed_file.EmbedFileData struct which we convert to string.
	embedded_file := $embed_file('temp_embed.txt')
	content := embedded_file.to_string()
	println('Embedded File Content:')
	println(content)

	// 4. $tmpl Directive (Compile-time Template Interpolation)
	println('\n--- 4. Template Interpolation (\$tmpl) ---')
	// Interpolates local variables inside the template file at compile time.
	name := 'Developer'
	status := 'active'

	// Renders the template with the variables in the current scope
	rendered_template := $tmpl('template.html')
	println('Rendered Template Output:')
	println(rendered_template)
}

Strings Builder

Strings Builder

V has a very rich and growing standard library and is actively updated. This lesson on Strings Builder showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of strings builder.

v
Run
module main

import strings

fn main() {
	// 1. Initialize a new Builder with pre-allocated buffer size (e.g. 100 bytes).
	// Pre-allocation is highly recommended for performance to reduce memory allocations.
	mut sb := strings.new_builder(100)

	// 2. Write strings and runes to the buffer
	sb.write_string('Welcome ')
	sb.write_string('to ')
	sb.write_string('the V standard library!')
	sb.write_rune(`\n`)

	sb.write_string('V is:\n')
	features := ['Fast', 'Simple', 'Statically Typed', 'Safe']
	for feature in features {
		sb.write_string('- ')
		sb.write_string(feature)
		sb.write_rune(`\n`)
	}

	// 3. Extract the final constructed string
	result := sb.str()
	println(result)

	// 4. Reset/Clear the builder to reuse it
	// In V, `clear()` clears the builder's buffer.
	sb.clear()
	sb.write_string('New content in builder.')
	println(sb.str())
}

Os Advanced Io

This example demonstrates advanced Unix file behaviors such as raw struct binary serialization, cursor seeking (seek/tell), file size truncation, and recursive directory tree traversal.


v
Run
module main

import os

struct Config {
mut:
	id   int
	val  f64
	name [20]u8 // fixed-size array of bytes for safe serialization
}

fn main() {
	println('=== V Advanced File I/O & Directory Walking ===')

	file_path := 'temp_advanced_io.bin'

	// --- 1. Struct Reading & Writing (Binary Serialization) ---
	println('\n--- 1. Struct Binary Serialization ---')

	// Create a mutable file in write/read mode
	mut f := os.open_file(file_path, 'w+') or {
		println('Failed to open file: ${err}')
		return
	}

	mut cfg := Config{
		id: 101
		val: 99.99
		name: [u8(0), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]!
	}

	// Populate name
	name_str := 'V-OS-Advanced-IO'
	for i in 0 .. name_str.len {
		if i < 20 {
			cfg.name[i] = name_str[i]
		}
	}

	// Write struct representation directly to the file
	f.write_struct(cfg) or {
		println('Failed to write struct: ${err}')
	}
	println('Struct successfully serialized to file.')


	// --- 2. Seeking & Cursor Position (seek/tell) ---
	println('\n--- 2. File Seeking & Cursor Position ---')

	// Retrieve current position in the file (should be size of struct)
	pos := f.tell() or { 0 }
	println('Current file cursor position: ${pos} bytes')

	// Seek back to the beginning of the file (.start)
	println('Seeking back to the start of the file...')
	f.seek(0, .start) or {
		println('Failed to seek: ${err}')
	}

	// Read struct back from file
	mut read_cfg := Config{
		name: [u8(0), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]!
	}
	f.read_struct(mut read_cfg) or {
		println('Failed to read struct: ${err}')
	}

	// Extract string from fixed-size byte array
	mut bytes := []u8{}
	for b in read_cfg.name {
		if b == 0 { break }
		bytes << b
	}
	name_read := bytes.bytestr()

	println('Deserialized Struct:')
	println('  ID:   ${read_cfg.id}')
	println('  Val:  ${read_cfg.val}')
	println('  Name: ${name_read}')

	f.close()


	// --- 3. Truncating Files ---
	println('\n--- 3. File Truncation (truncate) ---')

	// Note: V's os.truncate opens the file with O_TRUNC, resetting it first before sizing.
	// Shrinking/sizing a file directly using os.truncate:
	println('Truncating file "${file_path}" to 10 bytes...')
	os.truncate(file_path, 10) or {
		println('Failed to truncate: ${err}')
	}
	println('File size after truncation: ${os.file_size(file_path)} bytes')

	// Clean up binary file
	os.rm(file_path) or {}


	// --- 4. Recursive Directory Tree Walking ---
	println('\n--- 4. Directory Tree Walking (walk) ---')

	// Create a dummy tree for traversal
	walk_root := 'temp_walk_root'
	sub_dir := os.join_path(walk_root, 'docs')
	os.mkdir_all(sub_dir) or {}
	os.write_file(os.join_path(walk_root, 'file1.txt'), 'content1') or {}
	os.write_file(os.join_path(sub_dir, 'file2.log'), 'content2') or {}
	os.write_file(os.join_path(sub_dir, 'file3.txt'), 'content3') or {}

	// Recursive walk using a callback
	println('Recursive walk using os.walk (all files):')
	os.walk(walk_root, fn (path string) {
		println('  Found file: ${path}')
	})

	// Walk with file extension filter
	println('Walk with file extension filter using os.walk_ext (.txt only):')
	txt_files := os.walk_ext(walk_root, '.txt', os.WalkParams{})
	for path in txt_files {
		println('  Found .txt file: ${path}')
	}

	// Cleanup directory tree
	os.rmdir_all(walk_root) or {}
	println('Directory tree cleanup complete.')
}

Os Operations

This example demonstrates common file system tasks, path manipulation, working directory traversal, environmental querying, symbol links, permissions (chmod), and ownership (chown).

v
Run
module main

import os

fn main() {
	filename := 'temp_book_example.txt'
	content := 'V standard library makes OS operations very straightforward.'

	// ==========================================
	// 1. Basic File Operations (Writing, Reading, Existence)
	// ==========================================
	println('Writing text to ${filename}...')
	os.write_file(filename, content) or {
		println('Failed to write file: ${err}')
		return
	}

	// Checking file existence
	if os.exists(filename) {
		println('Confirmed: File exists.')
	}

	// Reading from a file
	read_content := os.read_file(filename) or {
		println('Failed to read file: ${err}')
		return
	}
	println('Read content from file: "${read_content}"')

	// Writing and reading lines
	lines := ['Line 1: V has simple OS functions.', 'Line 2: Supporting multiple lines.']
	lines_file := 'temp_lines_example.txt'
	os.write_lines(lines_file, lines) or {
		println('Failed to write lines: ${err}')
	}
	read_lines := os.read_lines(lines_file) or {
		println('Failed to read lines: ${err}')
		[]
	}
	println('Read lines: ${read_lines}')
	os.rm(lines_file) or {}

	// Listing directory contents
	println('Listing files in current directory:')
	files := os.ls('.') or {
		println('Failed to list directory: ${err}')
		[]
	}
	for file in files {
		// Filter and print temp file
		if file == filename {
			println('- Found file: ${file}')
		}
	}

	// Reading environment variables
	home_dir := os.getenv('HOME')
	println('User HOME directory: ${home_dir}')

	// Checking if a binary exists in the system PATH
	if os.exists_in_system_path('git') {
		println('Confirmed: Git executable exists in system PATH.')
	}

	// Executing OS commands
	// os.execute runs command in a subshell and returns a Result struct containing exit_code and output.
	println('Running command "uname"...')
	res := os.execute('uname')
	if res.exit_code == 0 {
		println('Operating System: ${res.output.trim_space()}')
	} else {
		println('Command execution failed with code ${res.exit_code}: ${res.output}')
	}

	// ==========================================
	// 2. Directory Tree Operations (Nix/CLI Focus)
	// ==========================================
	println('\n--- Directory Tree Operations ---')

	// Create nested directories (like `mkdir -p`)
	nested_dir := os.join_path('temp_parent', 'temp_child')
	println('Creating nested directory structure: ${nested_dir}...')
	os.mkdir_all(nested_dir) or {
		println('Failed to create directory structure: ${err}')
	}

	// ==========================================
	// 3. Path Manipulation & Extraction
	// ==========================================
	println('\n--- Path Manipulation & Extraction ---')
	sample_path := '/usr/local/bin/v.exe'
	println('Sample path: ${sample_path}')
	println('Directory:   ${os.dir(sample_path)}')      // /usr/local/bin
	println('Base name:   ${os.base(sample_path)}')     // v.exe
	println('Extension:   ${os.file_ext(sample_path)}') // .exe

	// ==========================================
	// 4. Working Directory Traversal
	// ==========================================
	println('\n--- Working Directory Traversal ---')
	original_wd := os.getwd()
	println('Original working directory: ${original_wd}')

	println('Changing directory to: temp_parent...')
	os.chdir('temp_parent') or {
		println('Failed to change directory: ${err}')
	}
	println('New working directory: ${os.getwd()}')

	// Change back to original directory
	os.chdir(original_wd) or {
		println('Failed to restore directory: ${err}')
	}

	// ==========================================
	// 5. Advanced File Operations (Copying, Moving)
	// ==========================================
	println('\n--- Advanced File Actions ---')
	copied_file := 'temp_book_copy.txt'
	moved_file := 'temp_book_moved.txt'

	println('Copying ${filename} to ${copied_file}...')
	os.cp(filename, copied_file) or {
		println('Failed to copy file: ${err}')
	}

	println('Moving ${copied_file} to ${moved_file}...')
	os.mv(copied_file, moved_file) or {
		println('Failed to move file: ${err}')
	}

	// ==========================================
	// 6. Symbolic Links & Nix-Specific Operations
	// ==========================================
	println('\n--- Nix-Specific Operations ---')
	symlink_name := 'temp_book_link.txt'

	// Create symlink
	println('Creating symbolic link from ${moved_file} to ${symlink_name}...')
	os.symlink(moved_file, symlink_name) or {
		println('Failed to create symlink: ${err}')
	}

	// Check if path is a link
	if os.is_link(symlink_name) {
		println('Confirmed: ${symlink_name} is a symbolic link.')
	}

	// Change file permissions (chmod)
	// 0o644 = Owner: read/write, Group: read, Others: read
	println('Setting file permissions to 0o644 (read/write for owner, read-only for others)...')
	os.chmod(moved_file, 0o644) or {
		println('Failed to change permissions: ${err}')
	}

	// Check permissions
	println('Is readable?   ${os.is_readable(moved_file)}')
	println('Is writable?   ${os.is_writable(moved_file)}')
	println('Is executable? ${os.is_executable(moved_file)}')

	// Change ownership (chown)
	// Safe demo using our current user's UID and GID to avoid permission errors
	uid := os.getuid()
	gid := os.getgid()
	println('Setting ownership of ${moved_file} to UID: ${uid}, GID: ${gid}...')
	os.chown(moved_file, uid, gid) or {
		println('Failed to change ownership: ${err}')
	}

	// ==========================================
	// 7. Cleanup
	// ==========================================
	println('\n--- Cleanup ---')

	// Remove original file
	os.rm(filename) or { println('Failed to remove ${filename}: ${err}') }

	// Remove moved file
	os.rm(moved_file) or { println('Failed to remove ${moved_file}: ${err}') }

	// Remove symlink
	os.rm(symlink_name) or { println('Failed to remove symlink ${symlink_name}: ${err}') }

	// Remove nested directory structure recursively
	os.rmdir_all('temp_parent') or { println('Failed to remove temp_parent directory: ${err}') }

	println('Cleanup completed successfully.')
}

Os Process Pipe

This example demonstrates managing subprocesses asynchronously using os.Process, exchanging data via stdin/stdout redirection, passing custom environments, sending POSIX signals (SIGSTOP, SIGCONT, SIGTERM), creating low-level descriptor pipes, and capturing stdout/stderr dynamically via IOCapture.

v
Run
module main

import os
import time

fn main() {
	println('=== V OS Processes, Pipes & Signals (POSIX/Nix) ===')

	// --- 1. Spawning and Controlling Processes ---
	println('\n--- 1. Asynchronous Child Process (Process) ---')

	// Spawning '/bin/cat' as a child process
	mut p := os.new_process('/bin/cat')
	p.set_args([])
	p.set_environment({
		'CUSTOM_ENV_VAR': 'V-OS-Demo'
	})

	// Enable standard I/O redirection to interact with the process
	p.set_redirect_stdio()
	p.use_stdio_ctl = true

	// Start the process asynchronously
	p.run()
	println('Child process spawned with PID: ${p.pid}')
	println('Is alive? -> ${p.is_alive()}')

	// Write to the process's standard input
	p.stdin_write('Line 1: Hello from the parent process!\n')
	p.stdin_write('Line 2: WebAssembly and V standard libraries rule.\n')

	// Allow child process buffer to receive and echo the lines
	time.sleep(100 * time.millisecond)

	// Read output currently available in the stdout pipe
	output := p.stdout_read()
	println('Read from child stdout:\n${output.trim_space()}')

	// --- 2. POSIX Signaling ---
	println('\n--- 2. POSIX Signals ---')

	// Suspend the child process (SIGSTOP)
	println('Suspending child process (SIGSTOP)...')
	p.signal_stop()
	time.sleep(50 * time.millisecond)

	// Resume the child process (SIGCONT)
	println('Resuming child process (SIGCONT)...')
	p.signal_continue()
	time.sleep(50 * time.millisecond)

	// Terminate the child process (SIGTERM)
	println('Terminating child process (SIGTERM)...')
	p.signal_term()
	p.wait()

	println('Child process exited with status: ${p.status} (Code: ${p.code})')
	p.close()


	// --- 3. Pipes ---
	println('\n--- 3. Low-Level Descriptor Pipes (Pipe) ---')

	// Create a new pipe
	mut my_pipe := os.pipe() or {
		println('Failed to create pipe: ${err}')
		return
	}

	// Write to the pipe
	pipe_msg := 'IPC via Pipe'.bytes()
	written := my_pipe.write(pipe_msg) or {
		println('Failed to write to pipe: ${err}')
		0
	}
	println('Wrote ${written} bytes to pipe.')

	// Read from the pipe
	mut pipe_buf := []u8{len: 32}
	bytes_read := my_pipe.read(mut pipe_buf) or {
		println('Failed to read from pipe: ${err}')
		0
	}
	println('Read message from pipe: "${pipe_buf[..bytes_read].bytestr()}"')
	my_pipe.close()


	// --- 4. Capture Stdout/Stderr ---
	println('\n--- 4. Capture Stdout and Stderr (IOCapture) ---')

	// Flush stdout to prevent capturing existing print statements
	os.flush()

	// Capture all stdout/stderr output within this block
	mut cap := os.stdio_capture() or {
		println('Failed to initialize capture: ${err}')
		return
	}

	// Anything printed here will be redirected to the capture buffer
	print('Captured standard output data.')
	eprint('Captured standard error data.')

	// Restore standard streams and retrieve captured data
	captured_out, captured_err := cap.finish()

	println('Captured stdout lines: ${captured_out}')
	println('Captured stderr lines: ${captured_err}')
}

Os System Info

This example demonstrates calling system diagnostics (os.uname), retrieving current host/user identities, assessing disk capacity and usage metrics (os.disk_usage), and parsing detailed file metadata using POSIX stat/lstat mappings (os.Stat and os.FileMode).

v
Run
module main

import os

fn main() {
	println('=== V OS System & File Information (POSIX/Nix) ===')

	// --- 1. System Info (uname, hostname, loginname) ---
	println('\n--- 1. System Diagnostics ---')

	// os.uname() returns kernel details, release, OS name, and architecture
	u := os.uname()
	println('Operating System:     ${u.sysname}')
	println('Node Name (Network):  ${u.nodename}')
	println('Kernel Release:       ${u.release}')
	println('Kernel Version:       ${u.version}')
	println('Machine Architecture: ${u.machine}')

	// Retrieve hostname and login user name
	host := os.hostname() or { 'unknown_host' }
	user := os.loginname() or { 'unknown_user' }
	println('Hostname:             ${host}')
	println('Login Name:           ${user}')


	// --- 2. Identity and Process Metrics ---
	println('\n--- 2. User/Group IDs & Process Context ---')

	// Real and effective UID/GIDs
	println('User ID (UID):        ${os.getuid()}')
	println('Group ID (GID):       ${os.getgid()}')
	println('Effective UID (EUID): ${os.geteuid()}')
	println('Effective GID (EGID): ${os.getegid()}')

	// Current Process ID and Parent Process ID
	println('Process ID (PID):     ${os.getpid()}')
	println('Parent PID (PPID):    ${os.getppid()}')


	// --- 3. Disk Space Usage ---
	println('\n--- 3. Disk Space Stats ---')

	// Query disk space information for the current directory
	du := os.disk_usage('.') or {
		println('Failed to retrieve disk usage: ${err}')
		return
	}

	// Convert u64 bytes to Gigabytes for user readability
	total_gb := f64(du.total) / (1024.0 * 1024.0 * 1024.0)
	avail_gb := f64(du.available) / (1024.0 * 1024.0 * 1024.0)
	used_gb := f64(du.used) / (1024.0 * 1024.0 * 1024.0)

	println('Disk Total:     ${total_gb:.2f} GB')
	println('Disk Available: ${avail_gb:.2f} GB')
	println('Disk Used:      ${used_gb:.2f} GB')


	// --- 4. Detailed File Metadata (stat/lstat) ---
	println('\n--- 4. File Metadata via stat ---')

	// Let's create a temporary file to run stat on
	temp_file := 'temp_stat_test.txt'
	os.write_file(temp_file, 'V stat demo content.') or { return }

	// Fetch file stats
	st := os.stat(temp_file) or {
		println('Failed to stat file: ${err}')
		os.rm(temp_file) or {}
		return
	}

	println('File Size:          ${st.size} bytes')
	println('Inode Number:       ${st.inode}')
	println('Hard Links Count:   ${st.nlink}')
	println('Device ID:          ${st.dev}')
	println('Owner UID:          ${st.uid}')
	println('Owner GID:          ${st.gid}')

	// Access access, modify, and status change timestamps
	println('Last Access Time:   ${st.atime} (Unix Epoch)')
	println('Last Modify Time:   ${st.mtime} (Unix Epoch)')
	println('Last Change Time:   ${st.ctime} (Unix Epoch)')

	// File type and permissions from Stat
	file_type := st.get_filetype()
	file_mode := st.get_mode()
	println('File Type:          ${file_type}') // e.g., regular, directory, link, etc.
	println('File Mode Bitmask:  0o${file_mode.bitmask():o}') // octal representation

	// Permissions breakdown
	println('Permissions -> Owner: R=${file_mode.owner.read} W=${file_mode.owner.write} X=${file_mode.owner.execute}')
	println('               Group: R=${file_mode.group.read} W=${file_mode.group.write} X=${file_mode.group.execute}')
	println('               Other: R=${file_mode.others.read} W=${file_mode.others.write} X=${file_mode.others.execute}')

	// Cleanup
	os.rm(temp_file) or {}
}

Time And Stopwatch

Time And Stopwatch

V has a very rich and growing standard library and is actively updated. This lesson on Time And Stopwatch showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of time and stopwatch.

v
Run
module main

import time

fn main() {
	// 1. Getting current local time
	now := time.now()
	println('Current local time: ${now}')
	println('Components -> Year: ${now.year}, Month: ${now.month}, Day: ${now.day}')

	// 2. Custom time formatting
	formatted := now.custom_format('YYYY-MM-DD HH:mm:ss')
	println('Custom formatted: ${formatted}')

	// 3. Time calculations (adding/subtracting durations)
	// V provides constants like time.hour, time.minute, time.second, etc.
	two_hours := 2 * time.hour
	future := now.add(two_hours)
	println('Time in 2 hours: ${future}')

	// 4. Measuring elapsed time using a Stopwatch
	println('Starting stopwatch...')
	mut sw := time.new_stopwatch()

	// Sleep for a short duration to simulate work
	time.sleep(150 * time.millisecond)

	elapsed := sw.elapsed()
	println('Elapsed time: ${elapsed.milliseconds()} ms')
}

Http Client

Http Client

V has a very rich and growing standard library and is actively updated. This lesson on Http Client showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of http client.

v
Run
module main

import net.http

fn main() {
	// 1. HTTP GET Request
	println('Sending GET request to vlang.io...')
	get_resp := http.get('https://vlang.io') or {
		println('GET request failed: ${err}')
		return
	}
	println('GET Status Code: ${get_resp.status_code}')

	// Reading a response header
	content_type := get_resp.header.get(.content_type) or { 'unknown' }
	println('GET Content-Type Header: ${content_type}')
	println('GET Body length: ${get_resp.body.len} bytes\n')

	// 2. HTTP POST Request
	println('Sending POST request to httpbin.org...')
	post_body := 'Hello V Standard Library!'
	post_resp := http.post('https://httpbin.org/post', post_body) or {
		println('POST request failed: ${err}')
		return
	}
	println('POST Status Code: ${post_resp.status_code}')
	println('POST Response Body:')
	println(post_resp.body)
}

Regex Matching

Regex Matching

V has a very rich and growing standard library and is actively updated. This lesson on Regex Matching showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of regex matching.

v
Run
module main

import regex

fn main() {
	// 1. Compile a regex pattern
	// r'...' specifies a raw string literal, avoiding excessive escaping
	mut re := regex.regex_opt(r'\d+') or {
		println('Failed to compile regex: ${err}')
		return
	}

	text := 'We have 15 apples, 32 bananas, and 120 oranges.'

	// 2. Find the first match in the text
	// `find()` searches anywhere in the string and returns (start_index, end_index)
	start, end := re.find(text)
	if start >= 0 {
		matched := text[start..end]
		println('First match found: "${matched}" at range (${start}, ${end})')
	} else {
		println('No match found.')
	}

	// 3. Find all matches in the text
	// `find_all_str()` returns an array of all matching substrings
	all_matches := re.find_all_str(text)
	println('All matches: ${all_matches}')

	// 4. Replace matches in the text
	// `replace()` replaces all occurrences matching the regex pattern
	replaced := re.replace(text, 'NUM')
	println('Replaced text: "${replaced}"')
}

Command Line Flags

Command Line Flags

V has a very rich and growing standard library and is actively updated. This lesson on Command Line Flags showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of command line flags.

v
Run
module main

import flag
import os

fn main() {
	// 1. Initialize the flag parser with command line arguments (os.args)
	mut fp := flag.new_flag_parser(os.args)
	fp.application('greet-tool')
	fp.version('1.0.0')
	fp.description("A simple CLI greeting utility demonstrating V's flag module.")

	// 2. Skip the executable name during parsing
	fp.skip_executable()

	// 3. Define flags with their types, short abbreviations, default values, and descriptions
	// The second argument is a u8 rune for the short flag (e.g. `n` for -n), or `0` for none.
	name := fp.string('name', `n`, 'Guest', 'The name of the person to greet')
	verbose := fp.bool('verbose', `v`, false, 'Enable verbose logging output')
	count := fp.int('count', `c`, 1, 'Number of times to print the greeting')

	// 4. Finalize parsing. This returns remaining non-flag arguments or an error.
	additional_args := fp.finalize() or {
		println('Error: ${err}')
		println(fp.usage())
		return
	}

	if verbose {
		println('Verbose Mode: ON')
		println('Parsing completed successfully.')
	}

	// 5. Use the parsed variables
	for i in 0 .. count {
		println('Hello, ${name}! (greeting ${i + 1}/${count})')
	}

	if additional_args.len > 0 {
		println('Additional non-flag arguments: ${additional_args}')
	}
}

Datatypes Collections

Datatypes Collections

V has a very rich and growing standard library and is actively updated. This lesson on Datatypes Collections showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of datatypes collections.

v
Run
module main

import datatypes

fn main() {
	// 1. Stack (LIFO - Last In First Out)
	println('=== Stack Demo ===')
	mut stack := datatypes.Stack[string]{}
	stack.push('first')
	stack.push('second')
	stack.push('third')
	println('Stack size: ${stack.len()}')
	println('Stack contents: ${stack.array()}')

	// peek() and pop() return Result (!T), so we handle with "or" block
	top := stack.peek() or { 'empty' }
	println('Peek top element: ${top}')

	for !stack.is_empty() {
		val := stack.pop() or { 'error' }
		println('Popped: ${val}')
	}

	// 2. Queue (FIFO - First In First Out)
	println('\n=== Queue Demo ===')
	mut queue := datatypes.Queue[int]{}
	queue.push(100)
	queue.push(200)
	queue.push(300)
	println('Queue size: ${queue.len()}')
	println('Queue contents: ${queue.array()}')

	// peek() and pop() return Result (!T)
	front := queue.peek() or { -1 }
	println('Peek front element: ${front}')

	for !queue.is_empty() {
		val := queue.pop() or { -1 }
		println('Dequeued: ${val}')
	}

	// 3. Set (Unique Elements)
	println('\n=== Set Demo ===')
	mut set_a := datatypes.Set[string]{}
	set_a.add_all(['apple', 'banana', 'cherry', 'apple']) // 'apple' is duplicate and ignored
	println('Set A elements: ${set_a.array()}')
	println('Set A size: ${set_a.size()}')
	println('Contains "banana": ${set_a.exists('banana')}')

	mut set_b := datatypes.Set[string]{}
	set_b.add_all(['cherry', 'date', 'elderberry'])
	println('Set B elements: ${set_b.array()}')

	// Union of Set A and Set B (note: 'union' is a V keyword, so we write '@union')
	union_set := set_a.@union(set_b)
	println('Union (A + B): ${union_set.array()}')

	// Intersection of Set A and Set B
	intersection_set := set_a.intersection(set_b)
	println('Intersection (A and B): ${intersection_set.array()}')

	// Difference of Set A and Set B (A - B)
	diff_set := set_a - set_b
	println('Difference (A - B): ${diff_set.array()}')
}

Gg Graphics

Gg Graphics

V has a very rich and growing standard library and is actively updated. This lesson on Gg Graphics showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of gg graphics using V's simple graphics module. It shows how to initialize a window, define an application state struct, draw various 2D shapes (rectangles, circles, triangles, polygons, lines), render formatted text, and intercept keyboard and mouse event inputs.

v
Run
module main

import gg
import math

// AppContext holds the state of our graphical application.
// Using a state struct is a recommended best practice for gg applications
// to avoid global variables (which V does not support by default).
struct AppContext {
mut:
	ctx          &gg.Context = unsafe { nil }
	width        int  = 800
	height       int  = 600
	// Interactive shape parameters
	shape_x      f32  = 400.0
	shape_y      f32  = 300.0
	shape_size   f32  = 50.0
	shape_color  gg.Color = gg.blue
	active_shape int  // 0 = Circle, 1 = Rectangle, 2 = Triangle
	// Tracking mouse positions and clicks
	mouse_x      f32
	mouse_y      f32
	click_x      f32  = -1.0
	click_y      f32  = -1.0
	click_color  gg.Color = gg.red
	// Last key pressed message
	last_key     string = 'None'
}

fn main() {
	// Initialize state
	mut app := &AppContext{
		width: 800
		height: 600
	}

	// Create a new gg context.
	// You specify callbacks for rendering frames, processing events,
	// and cleanups, along with initial window configurations.
	app.ctx = gg.new_context(
		width:        app.width
		height:       app.height
		window_title: "V's gg graphics module: Tutorial & Interactive Demo"
		bg_color:     gg.rgb(240, 244, 248) // A subtle modern light-blue background
		user_data:    app                 // Pass our state struct to be accessible inside callbacks
		frame_fn:     frame               // Callback called once per frame (to draw shapes/UI)
		event_fn:     on_event            // Callback called for user inputs (mouse & keyboard)
	)

	println('Starting interactive graphics window...')
	println('Controls:')
	println('  - Move the mouse to see cursor position tracking.')
	println('  - Left-Click anywhere to draw a red dot at the click location.')
	println('  - Use Arrow Keys (Up/Down/Left/Right) to move the active shape.')
	println('  - Press [C] or [c] to cycle the active shape\'s color.')
	println('  - Press [S] or [s] to toggle between Circle, Rectangle, and Triangle.')
	println('  - Press [Escape] to close the window.')
	println('\nReal-world Case Study:')
	println('  - Check out a complete journaling application built with gg:')
	println('    https://github.com/codecaine-zz/MindSpace-Journal')

	// Start the application main event loop.
	app.ctx.run()
}

// frame is the drawing function called per frame.
// All rendering code MUST reside between ctx.begin() and ctx.end().
fn frame(data voidptr) {
	mut app := unsafe { &AppContext(data) }
	mut ctx := app.ctx
	ctx.begin()

	// --- 1. Draw Static 2D Shapes ---

	// Draw a thick horizontal line dividing the header from the demo workspace
	ctx.draw_line(0, 80, app.width, 80, gg.gray)

	// Draw a filled rectangle (Left)
	ctx.draw_rect_filled(50, 120, 120, 80, gg.green)
	// Draw an empty/outline rectangle just next to it
	ctx.draw_rect_empty(200, 120, 120, 80, gg.dark_gray)

	// Draw a filled circle (Middle-Left)
	ctx.draw_circle_filled(430, 160, 45, gg.orange)
	// Draw an empty/outline circle
	ctx.draw_circle_empty(560, 160, 45, gg.purple)

	// Draw a filled triangle (Middle-Right)
	ctx.draw_triangle_filled(700, 115, 750, 205, 650, 205, gg.pink)

	// Draw a custom convex polygon (a star-like pentagon, bottom right)
	poly_points := [
		f32(650.0), 480.0, // Point 1
		750.0, 480.0,      // Point 2
		780.0, 560.0,      // Point 3
		700.0, 520.0,      // Point 4
		620.0, 560.0       // Point 5
	]
	ctx.draw_convex_poly(poly_points, gg.cyan)

	// --- 2. Render Text using custom configurations (TextCfg) ---

	// Main Title with default configuration
	ctx.draw_text_def(20, 15, "Welcome to V's Simple Graphics (gg) Tutorial!")

	// Instructions and state metadata at the top right
	ctx.draw_text(20, 45, 'Cursor: (${app.mouse_x:.1f}, ${app.mouse_y:.1f}) | Last Key: ${app.last_key}',
		color: gg.dark_blue
		size: 16
		bold: true
	)

	// Context description
	ctx.draw_text(50, 215, 'Filled & Empty Rectangles', size: 12, color: gg.dark_gray)
	ctx.draw_text(400, 215, 'Filled & Empty Circles', size: 12, color: gg.dark_gray)
	ctx.draw_text(650, 215, 'Filled Triangle', size: 12, color: gg.dark_gray)
	ctx.draw_text(630, 570, 'Convex Polygon (Pentagon)', size: 12, color: gg.dark_gray)

	// Help panel explaining key bindings
	ctx.draw_rect_filled(20, 440, 280, 140, gg.Color{r: 255, g: 255, b: 255, a: 180})
	ctx.draw_rect_empty(20, 440, 280, 140, gg.gray)
	ctx.draw_text(35, 450, 'Controls Panel', size: 15, bold: true, color: gg.black)
	ctx.draw_text(35, 475, '- Arrows: Move active shape', size: 13, color: gg.black)
	ctx.draw_text(35, 495, '- C: Cycle shape color', size: 13, color: gg.black)
	ctx.draw_text(35, 515, '- S: Switch shape type', size: 13, color: gg.black)
	ctx.draw_text(35, 535, '- Mouse Click: Draw a dot', size: 13, color: gg.black)
	ctx.draw_text(35, 555, '- Escape: Quit application', size: 13, color: gg.black)

	// Case Study reference
	ctx.draw_text(20, 585, 'Case Study: github.com/codecaine-zz/MindSpace-Journal', size: 10, italic: true, color: gg.dark_blue)

	// --- 3. Draw Dynamic / Interactive Elements ---

	// Draw a dot where the user clicked, if a click has occurred
	if app.click_x >= 0.0 {
		ctx.draw_circle_filled(app.click_x, app.click_y, 8, app.click_color)
		ctx.draw_circle_empty(app.click_x, app.click_y, 12, gg.black)
		ctx.draw_text(int(app.click_x) + 12, int(app.click_y) - 6, 'Last Click: (${app.click_x:.0f}, ${app.click_y:.0f})',
			size: 11
			color: gg.black
		)
	}

	// Draw the active shape controlled by the user
	match app.active_shape {
		0 {
			// Draw interactive Circle
			ctx.draw_circle_filled(app.shape_x, app.shape_y, app.shape_size, app.shape_color)
			ctx.draw_circle_empty(app.shape_x, app.shape_y, app.shape_size, gg.black)
		}
		1 {
			// Draw interactive Rectangle (centered on coordinates)
			half := app.shape_size
			ctx.draw_rect_filled(app.shape_x - half, app.shape_y - half, app.shape_size * 2, app.shape_size * 2, app.shape_color)
			ctx.draw_rect_empty(app.shape_x - half, app.shape_y - half, app.shape_size * 2, app.shape_size * 2, gg.black)
		}
		2 {
			// Draw interactive Equilateral Triangle (centered on coordinates)
			// Using trigonometry to draw an equilateral triangle of size `shape_size`
			h := app.shape_size * f32(math.sqrt(3.0)) / 2.0
			x1 := app.shape_x
			y1 := app.shape_y - (2.0 / 3.0) * h
			x2 := app.shape_x - app.shape_size
			y2 := app.shape_y + (1.0 / 3.0) * h
			x3 := app.shape_x + app.shape_size
			y3 := app.shape_y + (1.0 / 3.0) * h
			ctx.draw_triangle_filled(x1, y1, x2, y2, x3, y3, app.shape_color)
		}
		else {}
	}

	// Draw label above the active shape
	ctx.draw_text(int(app.shape_x) - 40, int(app.shape_y) - int(app.shape_size) - 20, 'Active Shape',
		size: 13
		bold: true
		color: gg.black
	)

	ctx.end()
}

// on_event intercepts and handles all system-level user inputs.
fn on_event(e &gg.Event, data voidptr) {
	mut app := unsafe { &AppContext(data) }
	mut ctx := app.ctx

	match e.typ {
		.mouse_move {
			app.mouse_x = e.mouse_x
			app.mouse_y = e.mouse_y
		}
		.mouse_down {
			app.click_x = e.mouse_x
			app.click_y = e.mouse_y
			// Randomize click dot color slightly for visual variety
			if app.click_color.r == 255 {
				app.click_color = gg.Color{r: 0, g: 180, b: 0, a: 255}
			} else {
				app.click_color = gg.red
			}
		}
		.key_down {
			app.last_key = e.key_code.str()

			match e.key_code {
				.escape {
					ctx.quit()
				}
				// Change Color when 'C' or 'c' is pressed
				.c {
					if app.shape_color.r == 0 && app.shape_color.b == 255 { // blue -> red
						app.shape_color = gg.red
					} else if app.shape_color.r == 255 && app.shape_color.g == 0 { // red -> green
						app.shape_color = gg.green
					} else { // green -> blue
						app.shape_color = gg.blue
					}
				}
				// Toggle shape type when 'S' or 's' is pressed
				.s {
					app.active_shape = (app.active_shape + 1) % 3
				}
				// Use Arrow Keys to move the active shape
				.left {
					app.shape_x -= 15.0
					if app.shape_x < 0 { app.shape_x = 0 }
				}
				.right {
					app.shape_x += 15.0
					if app.shape_x > app.width { app.shape_x = f32(app.width) }
				}
				.up {
					app.shape_y -= 15.0
					if app.shape_y < 80 { app.shape_y = 80 } // Keep below divider line
				}
				.down {
					app.shape_y += 15.0
					if app.shape_y > app.height { app.shape_y = f32(app.height) }
				}
				else {}
			}
		}
		else {}
	}
}

Command Line Arguments

This example demonstrates how to directly access and parse command-line arguments using os.args to build simple command-line applications.

v
Run
module main

import os

fn main() {
	// os.args is a []string containing all command line arguments.
	// os.args[0] is always the name of the executable (or the script path if run via v run).
	// os.args[1..] contains the actual command-line arguments passed to the program.
	println('Executable / script path: ${os.args[0]}')
	println('Total arguments count:    ${os.args.len}')
	println('All arguments list:       ${os.args}')

	if os.args.len < 2 {
		println('\nUsage: v run command_line_arguments.v <command> [arguments...]')
		println('Try running: v run command_line_arguments.v greet Alice')
		println('Try running: v run command_line_arguments.v sum 3 5 8')
		return
	}

	command := os.args[1]
	args := os.args[2..]

	println('\nProcessing command: "${command}" with args: ${args}')

	match command {
		'greet' {
			if args.len < 1 {
				println('Error: greet command requires a name.')
				return
			}
			name := args[0]
			println('Hello, ${name}!')
		}
		'sum' {
			if args.len < 1 {
				println('Error: sum command requires at least one number.')
				return
			}
			mut total := 0
			for arg in args {
				num := arg.int()
				total += num
			}
			println('Sum of numbers: ${total}')
		}
		else {
			println('Unknown command: "${command}". Allowed commands: "greet", "sum".')
		}
	}
}

Math And Rand

Math And Rand

V has a very rich and growing standard library and is actively updated. This lesson on Math And Rand showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of math and rand.

v
Run
module main

import math
import rand

fn main() {
	println('=== Math & Rand Module Examples ===')

	// --- math ---
	println('\n--- math ---')
	println('Pi constant: ${math.pi}')
	println('E constant:  ${math.e}')

	// Trigonometry
	angle := 45.0 * (math.pi / 180.0) // 45 degrees in radians
	println('sin(45 deg): ${math.sin(angle):.4f}')
	println('cos(45 deg): ${math.cos(angle):.4f}')
	println('tan(45 deg): ${math.tan(angle):.4f}')

	// Power, Square Root, Logarithms
	println('2^10:        ${math.pow(2.0, 10.0)}')
	println('sqrt(144):   ${math.sqrt(144.0)}')
	println('ln(e):       ${math.log(math.e)}')
	println('log10(100):  ${math.log10(100.0)}')

	// Absolute, Min/Max, Rounding
	println('abs(-5.5):   ${math.abs(-5.5)}')
	println('max(10, 20): ${math.max(10.0, 20.0)}')
	println('min(10, 20): ${math.min(10.0, 20.0)}')
	println('ceil(4.2):   ${math.ceil(4.2)}')
	println('floor(4.8):  ${math.floor(4.8)}')
	println('round(4.5):  ${math.round(4.5)}')

	// --- rand ---
	println('\n--- rand ---')
	// Random integers and floats
	random_int := rand.int_in_range(1, 100) or { 0 }
	println('Random integer in [1, 100): ${random_int}')

	random_f64 := rand.f64()
	println('Random f64 in [0.0, 1.0):   ${random_f64:.4f}')

	// Random boolean simulated using rand.intn
	random_bool := (rand.intn(2) or { 0 }) == 0
	println('Random boolean:             ${random_bool}')

	// Choosing a random element from an array
	items := ['Apple', 'Banana', 'Cherry', 'Date']
	chosen := rand.element(items) or { 'None' }
	println('Randomly chosen fruit:      ${chosen}')

	// Random UUID generation (commonly used)
	uuid_str := rand.uuid_v4()
	println('Random UUID v4:             ${uuid_str}')
}

Crypto Asymmetric

Crypto Asymmetric

V has a very rich and growing standard library and is actively updated. This lesson on Crypto Asymmetric showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

v
Run
module main

import crypto.ecdsa
import crypto.ed25519
import crypto.pem

fn main() {
	println('=== V Asymmetric Cryptography Demo ===')

	message := 'Message to sign and verify asymmetric signatures.'.bytes()

	// --- 1. ECDSA ---
	println('\n--- ECDSA ---')

	// Generate key pair
	pub_ec, priv_ec := ecdsa.generate_key() or {
		println('Failed to generate ECDSA key: ${err}')
		return
	}

	// Sign message
	sig_ec := priv_ec.sign(message, ecdsa.SignerOpts{}) or {
		println('ECDSA signing failed: ${err}')
		return
	}
	println('ECDSA Signature (Hex): ${sig_ec.hex()}')

	// Verify message
	verified_ec := pub_ec.verify(message, sig_ec, ecdsa.SignerOpts{}) or {
		println('ECDSA verification error: ${err}')
		return
	}
	println('ECDSA Signature Verified? -> ${verified_ec}')


	// --- 2. Ed25519 ---
	println('\n--- Ed25519 ---')

	// Generate key pair
	pub_ed, priv_ed := ed25519.generate_key() or {
		println('Failed to generate Ed25519 key: ${err}')
		return
	}

	// Sign message
	sig_ed := ed25519.sign(priv_ed, message) or {
		println('Ed25519 signing failed: ${err}')
		return
	}
	println('Ed25519 Signature (Hex): ${sig_ed.hex()}')

	// Verify message
	verified_ed := ed25519.verify(pub_ed, message, sig_ed) or {
		println('Ed25519 verification error: ${err}')
		return
	}
	println('Ed25519 Signature Verified? -> ${verified_ed}')


	// --- 3. PEM Encoding/Decoding ---
	println('\n--- PEM (Privacy Enhanced Mail) Encoding ---')

	pub_bytes := pub_ec.bytes() or {
		println('Failed to get public key bytes: ${err}')
		return
	}

	mut pem_block := pem.Block.new('EC PUBLIC KEY')
	pem_block.data = pub_bytes

	pem_string := pem_block.encode(pem.EncodeConfig{}) or {
		println('PEM encoding failed: ${err}')
		return
	}
	println('Encoded PEM Public Key:')
	println(pem_string)

	// Decode back
	decoded_block, _ := pem.decode(pem_string) or {
		println('PEM decoding failed')
		return
	}
	println('Decoded Block Type: "${decoded_block.block_type}"')
	println('Decoded data size matches? -> ${decoded_block.data.len == pub_bytes.len}')
}

Crypto Entropy

Crypto Entropy

V has a very rich and growing standard library and is actively updated. This lesson on Crypto Entropy showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

v
Run
module main

import crypto.rand
import math.big

fn main() {
	println('=== V Secure Randomness (Entropy) Demo ===')

	// --- 1. Generating Secure Random Bytes ---
	println('\n--- Secure Random Bytes ---')
	// Generates securely generated random bytes from the OS entropy pool
	random_bytes := rand.bytes(16) or {
		println('Failed to generate secure bytes: ${err}')
		return
	}
	println('Generated 16 secure bytes (Hex): ${random_bytes.hex()}')

	// --- 2. Generating Secure Random u64 ---
	println('\n--- Secure Random u64 ---')
	// Generates a random u64 in the range [0, max)
	limit_u64 := u64(10_000)
	random_val := rand.int_u64(limit_u64) or {
		println('Failed to generate random u64: ${err}')
		return
	}
	println('Secure random u64 in [0, ${limit_u64}): ${random_val}')

	// --- 3. Generating Secure Random Big Integer ---
	println('\n--- Secure Random big.Integer ---')
	// Generates a random big.Integer in the range [0, limit)
	limit_str := '10000000000000000000000000000000000000000' // 10^40
	limit_big := big.integer_from_string(limit_str) or {
		println('Failed to parse big integer string: ${err}')
		return
	}

	random_big := rand.int_big(limit_big) or {
		println('Failed to generate random big integer: ${err}')
		return
	}
	println('Secure random big.Integer in [0, 10^40):')
	println(random_big.str())
}

Crypto Hash

For a detailed demonstration of every cryptographic module, the standard library examples are structured into subdirectories under languageupdatesandstdlib/02standardlibrary/11logandcrypto/crypto/.

1. Cryptographic Hash Functions

Demonstrates MD5, SHA-1, SHA-256, SHA-512, SHA-3 (Keccak-256/Keccak-512), RIPEMD-160, BLAKE2b, BLAKE2s, and BLAKE3.

2. Symmetric Ciphers & Block Modes

Demonstrates AES (CBC block mode with PKCS7-like padding), DES, Blowfish (encryption-only), RC4 stream cipher, and general block modes.

3. Asymmetric Cryptography & PEM Formats

Demonstrates ECDSA key generation, signing, and verification; Ed25519 signing and verification; and PEM block encoding/decoding.

4. Key Derivation Functions (KDF)

Demonstrates secure password hashing and key derivation using Bcrypt, Scrypt, and PBKDF2.

5. Message Authentication Codes (MAC)

Demonstrates message integrity and authenticity verification using HMAC-SHA256.

6. Secure Randomness & Entropy

Demonstrates generating secure cryptographically random bytes, u64 values, and large integers (big.Integer).

v
Run
module main

import crypto.md5
import crypto.sha1
import crypto.sha256
import crypto.sha512
import crypto.sha3
import crypto.ripemd160
import crypto.blake2b
import crypto.blake2s
import crypto.blake3

fn main() {
	println('=== V Cryptographic Hash Algorithms ===')

	input := 'V Language Crypto Guide'.bytes()
	input_str := 'V Language Crypto Guide'

	// 1. MD5 (128-bit)
	md5_hex := md5.hexhash(input_str)
	println('MD5:       ${md5_hex}')

	// 2. SHA-1 (160-bit)
	sha1_hex := sha1.hexhash(input_str)
	println('SHA-1:     ${sha1_hex}')

	// 3. SHA-256 (256-bit)
	sha256_hex := sha256.hexhash(input_str)
	println('SHA-256:   ${sha256_hex}')

	// 4. SHA-512 (512-bit)
	sha512_hex := sha512.hexhash(input_str)
	println('SHA-512:   ${sha512_hex}')

	// 5. SHA-3 (Keccak-based, 256 and 512 bit sums)
	sha3_256 := sha3.sum256(input)
	sha3_512 := sha3.sum512(input)
	println('SHA3-256:  ${sha3_256.hex()}')
	println('SHA3-512:  ${sha3_512.hex()}')

	// 6. RIPEMD-160 (160-bit)
	ripemd_hex := ripemd160.hexhash(input_str)
	println('RIPEMD160: ${ripemd_hex}')

	// 7. BLAKE2b (commonly 512-bit / 256-bit)
	blake2b_256 := blake2b.sum256(input)
	blake2b_512 := blake2b.sum512(input)
	println('BLAKE2b-256: ${blake2b_256.hex()}')
	println('BLAKE2b-512: ${blake2b_512.hex()}')

	// 8. BLAKE2s (commonly 256-bit)
	blake2s_256 := blake2s.sum256(input)
	println('BLAKE2s-256: ${blake2s_256.hex()}')

	// 9. BLAKE3 (256-bit, highly optimized)
	blake3_256 := blake3.sum256(input)
	println('BLAKE3-256:  ${blake3_256.hex()}')
}

Crypto Kdf

Crypto Kdf

V has a very rich and growing standard library and is actively updated. This lesson on Crypto Kdf showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

v
Run
module main

import crypto.bcrypt
import crypto.scrypt
import crypto.pbkdf2
import crypto.sha256

fn main() {
	println('=== V Key Derivation Functions Demo ===')

	// --- 1. Bcrypt ---
	println('\n--- Bcrypt ---')
	password := 'super_secure_password'.bytes()
	hash := bcrypt.generate_from_password(password, bcrypt.default_cost) or {
		println('Bcrypt failed: ${err}')
		return
	}
	println('Bcrypt hash: ${hash}')

	bcrypt.compare_hash_and_password(password, hash.bytes()) or {
		println('Bcrypt verification failed: ${err}')
		return
	}
	println('Bcrypt verification successful!')


	// --- 2. Scrypt ---
	println('\n--- Scrypt ---')
	scrypt_pass := 'my_scrypt_pass'.bytes()
	scrypt_salt := 'scrypt_salt'.bytes()

	// N=16384, r=8, p=1, key_len=32 (N must be power of 2)
	scrypt_key := scrypt.scrypt(scrypt_pass, scrypt_salt, 16384, 8, 1, 32) or {
		println('Scrypt failed: ${err}')
		return
	}
	println('Scrypt Key (Hex): ${scrypt_key.hex()}')


	// --- 3. PBKDF2 ---
	println('\n--- PBKDF2 ---')
	pbkdf2_pass := 'my_pbkdf2_pass'.bytes()
	pbkdf2_salt := 'pbkdf2_salt'.bytes()

	// pbkdf2.key(password, salt, iterations, key_len, hash_fn)
	pbkdf2_key := pbkdf2.key(pbkdf2_pass, pbkdf2_salt, 4096, 32, sha256.new()) or {
		println('PBKDF2 failed: ${err}')
		return
	}
	println('PBKDF2 Key (Hex): ${pbkdf2_key.hex()}')
}

Crypto Mac

Crypto Mac

V has a very rich and growing standard library and is actively updated. This lesson on Crypto Mac showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

v
Run
module main

import crypto.hmac
import crypto.sha256

fn main() {
	println('=== V Message Authentication Codes (MAC) Demo ===')

	// --- 1. HMAC-SHA256 Signature Generation ---
	println('\n--- HMAC-SHA256 Signature ---')
	key := 'secret_signing_key'.bytes()
	message := 'This is a message to be authenticated using HMAC.'.bytes()

	// hmac.new(key, data, hash_func, blocksize)
	mac := hmac.new(key, message, sha256.sum, sha256.block_size)
	println('HMAC (Hex): ${mac.hex()}')

	// --- 2. HMAC Verification ---
	println('\n--- HMAC Verification ---')

	// Re-compute to verify
	computed_mac := hmac.new(key, message, sha256.sum, sha256.block_size)

	// hmac.equal performs constant-time comparison to prevent timing attacks
	is_valid := hmac.equal(mac, computed_mac)
	println('Signature matches? -> ${is_valid}')

	// Verify with a tampered message
	tampered_message := 'This is a message to be authenticated using HMAC!'.bytes()
	tampered_mac := hmac.new(key, tampered_message, sha256.sum, sha256.block_size)
	is_tampered_valid := hmac.equal(mac, tampered_mac)
	println('Tampered signature matches? -> ${is_tampered_valid}')
}

Crypto Symmetric

Crypto Symmetric

V has a very rich and growing standard library and is actively updated. This lesson on Crypto Symmetric showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

v
Run
module main

import crypto.aes
import crypto.des
import crypto.blowfish
import crypto.rc4
import crypto.cipher

fn main() {
	println('=== V Symmetric Cryptography Demo ===')

	// --- 1. AES with CBC Block Mode ---
	println('\n--- AES (CBC Mode) ---')
	aes_key := [u8(1), 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] // 16-byte key (AES-128)
	aes_iv := [u8(9), 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, 6, 5, 4]       // 16-byte IV

	aes_block := aes.new_cipher(aes_key)
	mut aes_enc := cipher.new_cbc(aes_block, aes_iv)

	// In CBC mode, data must be padded to block size (16 bytes for AES)
	plaintext := 'Hello, V Cryptography! Padding here.'.bytes() // 36 bytes. We need to pad it to 48 bytes (multiple of 16)
	mut padded := plaintext.clone()
	pad_len := 16 - (padded.len % 16)
	for _ in 0 .. pad_len {
		padded << u8(pad_len)
	}

	mut ciphertext := []u8{len: padded.len}
	aes_enc.encrypt_blocks(mut ciphertext, padded)
	println('Ciphertext (Hex): ${ciphertext.hex()}')

	// Decrypt
	mut aes_dec := cipher.new_cbc(aes_block, aes_iv)
	mut decrypted := []u8{len: ciphertext.len}
	aes_dec.decrypt_blocks(mut decrypted, ciphertext)

	// Unpad
	unpadded_len := decrypted.len - int(decrypted.last())
	unpadded_text := decrypted[..unpadded_len].bytestr()
	println('Decrypted Text:   "${unpadded_text}"')


	// --- 2. DES Block Cipher ---
	println('\n--- DES ---')
	des_key := [u8(1), 2, 3, 4, 5, 6, 7, 8] // 8-byte key
	des_block := des.new_cipher(des_key)

	des_plain := 'DESplain'.bytes() // exactly 8 bytes (DES block size)
	mut des_cipher := []u8{len: 8}
	des_block.encrypt(mut des_cipher, des_plain)
	println('DES Ciphertext (Hex): ${des_cipher.hex()}')

	mut des_decrypted := []u8{len: 8}
	des_block.decrypt(mut des_decrypted, des_cipher)
	println('DES Decrypted:        "${des_decrypted.bytestr()}"')


	// --- 3. Blowfish Block Cipher ---
	println('\n--- Blowfish (Encryption Only) ---')
	bf_key := 'blowfish_key'.bytes()
	mut bf := blowfish.new_cipher(bf_key) or { panic(err) }

	bf_plain := 'bf_block'.bytes() // exactly 8 bytes (Blowfish block size)
	mut bf_cipher := []u8{len: 8}
	bf.encrypt(mut bf_cipher, bf_plain)
	println('Blowfish Ciphertext (Hex): ${bf_cipher.hex()}')
	println('(Note: V standard library crypto.blowfish only supports encryption)')


	// --- 4. RC4 Stream Cipher ---
	println('\n--- RC4 (Stream Cipher) ---')
	rc4_key := 'rc4_secret_key'.bytes()
	rc4_plain := 'RC4 is a stream cipher commonly used for legacy operations.'.bytes()

	mut rc4_enc := rc4.new_cipher(rc4_key) or { panic(err) }
	mut rc4_cipher := []u8{len: rc4_plain.len}
	rc4_enc.xor_key_stream(mut rc4_cipher, rc4_plain)
	println('RC4 Ciphertext (Hex): ${rc4_cipher.hex()}')

	mut rc4_dec := rc4.new_cipher(rc4_key) or { panic(err) }
	mut rc4_decrypted := []u8{len: rc4_cipher.len}
	rc4_dec.xor_key_stream(mut rc4_decrypted, rc4_cipher)
	println('RC4 Decrypted:        "${rc4_decrypted.bytestr()}"')
}

Log And Crypto

Log And Crypto

V has a very rich and growing standard library and is actively updated. This lesson on Log And Crypto showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of log and crypto.

v
Run
module main

import log
import crypto.sha256
import crypto.md5

fn main() {
	println('=== Log & Crypto Module Examples ===')

	// --- log ---
	println('\n--- log ---')
	// V's log module provides customizable levels (debug, info, warn, error, fatal)
	mut logger := log.Log{}
	logger.set_level(.info) // Set threshold (ignores debug level)

	logger.info('Logger initialized.')
	logger.warn('This is a warning message.')
	logger.error('This is an error message.')

	// --- crypto ---
	println('\n--- crypto ---')
	input := 'V language standard library'

	// SHA256 Hash
	sha_hash := sha256.hexhash(input)
	println('SHA-256 of "${input}":')
	println('  ${sha_hash}')

	// MD5 Hash
	md5_hash := md5.hexhash(input)
	println('MD5 of "${input}":')
	println('  ${md5_hash}')
}

Sync Concurrency

Sync Concurrency

V supports lightweight concurrency using v-routines via the spawn keyword (which spawns a function in a new thread). Threads communicate safely using channels, which prevent race conditions. For shared memory concurrency, V provides the shared keyword alongside lock and unlock blocks to safely synchronize access to variables.

These examples cover spawning tasks, reading/writing channels, buffering, select statements, and thread synchronization.

Additional Context from Repository docs:

This example demonstrates the concepts of sync concurrency.

v
Run
module main

import sync
import time

fn worker(id int, mut wg sync.WaitGroup) {
	defer {
		wg.done()
	}
	println('Worker ${id} starting...')
	time.sleep(50 * time.millisecond)
	println('Worker ${id} done!')
}

fn main() {
	println('=== Sync & Concurrency Examples ===')

	// 1. WaitGroup (Wait for multiple goroutines/tasks)
	mut wg := sync.new_waitgroup()
	for i in 1 .. 4 {
		wg.add(1)
		go worker(i, mut wg)
	}
	wg.wait()
	println('All workers completed!')

	// 2. Mutex (Thread-safe shared state access)
	println('\n=== Mutex Demo ===')
	mut mu := sync.new_mutex()
	mu.@lock()
	println('Mutex locked')
	mu.unlock()
	println('Mutex unlocked')
}

Encoding Formats

Encoding Formats

V has a very rich and growing standard library and is actively updated. This lesson on Encoding Formats showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of encoding formats.

v
Run
module main

import encoding.base64
import encoding.hex
import encoding.csv

fn main() {
	println('=== Encoding Modules Examples ===')

	// --- 1. Base64 ---
	println('\n--- Base64 ---')
	raw_str := 'V Programming Language'
	encoded_b64 := base64.encode_str(raw_str)
	println('Encoded Base64: ${encoded_b64}')

	decoded_b64 := base64.decode_str(encoded_b64)
	println('Decoded Base64: ${decoded_b64}')

	// --- 2. Hex ---
	println('\n--- Hex ---')
	raw_bytes := [u8(72), 101, 108, 108, 111] // "Hello"
	encoded_hex := hex.encode(raw_bytes)
	println('Encoded Hex:    ${encoded_hex}')

	decoded_hex := hex.decode(encoded_hex) or { []u8{} }
	println('Decoded Hex:    ${decoded_hex.bytestr()}')

	// --- 3. CSV ---
	println('\n--- CSV ---')
	csv_data := 'Name,Age,City\nAlice,30,New York\nBob,25,San Francisco'

	mut reader := csv.new_reader(csv_data)
	println('Reading CSV rows:')
	for {
		row := reader.read() or { break }
		println('  Row: ${row}')
	}
}

Arrays Utility

Arrays Utility

V has a very rich and growing standard library and is actively updated. This lesson on Arrays Utility showcases modern standard library packages, system calls, network sockets, inline assembly, or WASM support.

Additional Context from Repository docs:

This example demonstrates the concepts of arrays utility.

v
Run
module main

import arrays

fn main() {
	println('=== arrays Utility Module Examples ===')

	nums := [5, 3, 9, 1, 7, 3]

	// Find min and max
	min_val := arrays.min(nums) or { 0 }
	max_val := arrays.max(nums) or { 0 }
	println('Array: ${nums}')
	println('Min:   ${min_val}') // 1
	println('Max:   ${max_val}') // 9

	// Find index of min/max
	min_idx := arrays.idx_min(nums) or { -1 }
	max_idx := arrays.idx_max(nums) or { -1 }
	println('Index of Min: ${min_idx}') // 3
	println('Index of Max: ${max_idx}') // 2

	// Chunking
	chunked := arrays.chunk(nums, 2)
	println('Chunked into sizes of 2: ${chunked}') // [[5, 3], [9, 1], [7, 3]]

	// Uniq (remove consecutive duplicates)
	consecutive_dups := [1, 1, 2, 2, 3, 1, 1]
	unique := arrays.uniq(consecutive_dups)
	println('Consecutive duplicates array: ${consecutive_dups}')
	println('After uniq():                 ${unique}') // [1, 2, 3, 1]
}

Toml

This example demonstrates how to parse and query TOML configuration files using V's built-in toml module.

v
Run
module main

import toml

const toml_content = '
# TOML configuration example
title = "V TOML Demo"

[owner]
name = "Antigravity AI"
organization = "Google DeepMind"

[database]
server = "127.0.0.1"
ports = [ 5432, 5433 ]
connection_max = 1000
enabled = true
'

fn main() {
	println('=== TOML Module Demo ===')
	doc := toml.parse_text(toml_content) or {
		println('Failed to parse TOML: ${err}')
		return
	}

	// 1. Reading basic values using value() and type converters
	title := doc.value('title').string()
	println('Project Title: ${title}')

	// 2. Accessing nested tables
	owner_name := doc.value('owner.name').string()
	org := doc.value('owner.organization').string()
	println('Owner: ${owner_name} (${org})')

	// 3. Accessing primitive values
	server := doc.value('database.server').string()
	conn_max := doc.value('database.connection_max').int()
	enabled := doc.value('database.enabled').bool()
	println('DB Server: ${server} | Connection Max: ${conn_max} | Enabled: ${enabled}')

	// 4. Retrieving array values
	ports_any := doc.value('database.ports')
	println('Ports Any: ${ports_any}')

	// Accessing array elements with query syntax
	port_0 := doc.value('database.ports[0]').int()
	port_1 := doc.value('database.ports[1]').int()
	println('Primary Port: ${port_0} | Secondary Port: ${port_1}')

	// 5. Using default values for non-existing keys
	db_timeout := doc.value('database.timeout').default_to(30).int()
	println('Database Timeout (Default): ${db_timeout} seconds')

	// 6. Optional retrieval using value_opt()
	if db_server := doc.value_opt('database.server') {
		println('Optional check: Database server key exists. Value = ${db_server.string()}')
	} else {
		println('Optional check: Database server key does not exist.')
	}
}

Strconv

This example demonstrates how to convert strings to numbers, parse numbers in different bases and bit-sizes, and convert numbers back to base string representations using the strconv module.

v
Run
module main

import strconv

fn main() {
	println('=== strconv Module Demo ===')

	// 1. Convert string to integer types (with error propagation/handling)
	val_int := strconv.atoi('12345') or {
		println('Error parsing int: ${err}')
		0
	}
	println('Parsed int: ${val_int}')

	val_i64 := strconv.atoi64('9223372036854775807') or {
		println('Error parsing i64: ${err}')
		0
	}
	println('Parsed i64: ${val_i64}')

	// 2. Parse unsigned and specific bases/bit-sizes
	// parse_int(s string, base int, bit_size int) !i64
	val_hex := strconv.parse_int('0xff', 0, 64) or {
		println('Error parsing hex: ${err}')
		0
	}
	println('Parsed hex (0xff in base 0): ${val_hex}')

	val_bin := strconv.parse_uint('101010', 2, 32) or {
		println('Error parsing binary: ${err}')
		0
	}
	println('Parsed binary (101010 in base 2): ${val_bin}')

	// 3. Convert string to float (atof64)
	val_f64 := strconv.atof64('3.14159265') or {
		println('Error parsing f64: ${err}')
		0.0
	}
	println('Parsed float64: ${val_f64}')

	// 4. Convert number to base string representation
	// format_int(n i64, radix int) string
	binary_str := strconv.format_int(42, 2)
	hex_str := strconv.format_int(255, 16)
	println('42 in binary: ${binary_str}')
	println('255 in hex:   ${hex_str}')
}

Term

This example demonstrates styling terminal output (bold, underline, strikethrough), coloring foreground and background text, and retrieving the terminal size using the term module.

v
Run
module main

import term

fn main() {
	println('=== term Module Demo ===')

	// 1. Get terminal size
	width, height := term.get_terminal_size()
	println('Terminal size: ${width} columns x ${height} rows')

	// 2. Colored text helpers
	println(term.green('This text is green!'))
	println(term.red('This text is red!'))
	println(term.yellow('This text is yellow!'))
	println(term.blue('This text is blue!'))

	// 3. Text styling modifiers
	println(term.bold('This text is bold!'))
	println(term.underline('This text is underlined!'))
	println(term.strikethrough('This text has a strikethrough!'))

	// 4. Background styling
	println(term.bg_blue(' This has a blue background! '))

	// 5. Message box helper formats
	println(term.ok_message('Operation succeeded!'))
	println(term.warn_message('This is a warning!'))
	println(term.fail_message('Operation failed!'))
}

Benchmark

This example demonstrates timing code execution chunks and step-by-step progress benchmarking using the benchmark module.

v
Run
module main

import benchmark
import time

fn main() {
	println('=== benchmark Module Demo ===')

	// Example 1: Using benchmark.start() and measure()
	println('--- Simple Measurement ---')
	mut b := benchmark.start()

	// Simulate work chunk 1
	time.sleep(50 * time.millisecond)
	b.measure('Simulated task 1 (50ms sleep)')

	// Simulate work chunk 2
	time.sleep(100 * time.millisecond)
	b.measure('Simulated task 2 (100ms sleep)')

	// Example 2: Using structured new_benchmark()
	println('\n--- Structured Step-by-Step Benchmarking ---')
	mut bmark := benchmark.new_benchmark()

	// Step 1: Ok step
	bmark.step()
	time.sleep(30 * time.millisecond)
	bmark.ok()
	println(bmark.step_message('Step 1 (successful arithmetic)'))

	// Step 2: Failed step demo
	bmark.step()
	time.sleep(10 * time.millisecond)
	bmark.fail()
	println(bmark.step_message('Step 2 (simulated failure verification)'))

	// Finalize and print results summary
	bmark.stop()
	println(bmark.total_message('Final summary of execution stages'))
}

Clipboard

This example demonstrates writing to and reading from the system clipboard on macOS using the clipboard module, including a backup and restore mechanism.

v
Run
module main

import clipboard

fn main() {
	println('=== clipboard Module Demo ===')

	// 1. Initialize clipboard
	mut cb := clipboard.new()
	defer {
		cb.destroy()
	}

	if !cb.is_available() {
		println('Clipboard is not available on this platform/session.')
		return
	}

	// 2. Backup current clipboard content so we do not overwrite user data permanently
	original_text := cb.paste()
	println('Backed up original clipboard text (length: ${original_text.len})')

	// 3. Copy new text to clipboard
	test_message := 'Hello from Vlang standard library!'
	println('Copying text to clipboard: "${test_message}"')
	if cb.copy(test_message) {
		println('Text successfully copied!')
	} else {
		println('Failed to copy text.')
	}

	// 4. Paste back to verify
	pasted_text := cb.paste()
	println('Pasted text from clipboard:  "${pasted_text}"')

	// 5. Restore original clipboard content
	println('Restoring original clipboard content...')
	cb.copy(original_text)
	println('Clipboard restore completed successfully.')
}

Semver

This example demonstrates parsing semantic versions and checking constraint satisfaction using the semver module.

v
Run
module main

import semver

fn main() {
	println('=== semver Module Demo ===')

	// 1. Parsing semver strings
	v1 := semver.from('1.5.0-beta.1+build.123') or {
		println('Failed to parse version: ${err}')
		return
	}
	v2 := semver.from('1.5.0') or {
		println('Failed to parse version: ${err}')
		return
	}
	v3 := semver.from('2.0.0-rc.1') or {
		println('Failed to parse version: ${err}')
		return
	}

	// 2. Accessing parts of the version struct
	println('Version 1 components:')
	println('  Raw string: ${v1.str()}')
	println('  Major:      ${v1.major}')
	println('  Minor:      ${v1.minor}')
	println('  Patch:      ${v1.patch}')
	println('  Prerelease: ${v1.prerelease}')
	println('  Build info: ${v1.metadata}')

	// 3. Comparisons using relational operators
	println('\nVersion Comparisons:')
	println('  ${v1} < ${v2} ? -> ${v1 < v2}')
	println('  ${v2} > ${v1} ? -> ${v2 > v1}')
	println('  ${v3} >= ${v2} ? -> ${v3 >= v2}')

	// 4. Checking against version ranges (constraints)
	println('\nVersion Constraint Satisfactions:')
	// Range checking
	println('  Is ${v2} in range ">=1.0.0 <2.0.0" ? -> ${v2.satisfies('>=1.0.0 <2.0.0')}')
	println('  Is ${v3} in range ">=1.0.0 <2.0.0" ? -> ${v3.satisfies('>=1.0.0 <2.0.0')}')

	// Complex constraint checking using logical OR (||)
	range_query := '^1.4.0 || >=2.0.0'
	println('  Does ${v2} satisfy "${range_query}"? -> ${v2.satisfies(range_query)}')
	println('  Does ${v3} satisfy "${range_query}"? -> ${v3.satisfies(range_query)}')
}

Maps Standard Library Module (maps.v)

This example demonstrates map utility functions such as filtering, converting map keys and values to arrays, inverting maps, and merging maps using the maps module.

v
Run
module main

import maps

fn main() {
	println('=== maps Module Demo ===')

	m1 := {
		'apple':  1
		'banana': 2
		'cherry': 3
	}

	// 1. Filter elements by condition
	filtered := maps.filter(m1, fn (k string, v int) bool {
		return v > 1
	})
	println('Filtered (values > 1): ${filtered}')

	// 2. Transform map entries to an array
	keys_upper := maps.to_array(m1, fn (k string, v int) string {
		return k.to_upper()
	})
	println('Transformed keys to upper array: ${keys_upper}')

	// 3. Invert map (swap keys and values)
	inverted := maps.invert(m1)
	println('Inverted map: ${inverted}')

	// 4. Construct a map from an array
	fruits := ['apple', 'banana', 'cherry']
	map_from_arr := maps.from_array(fruits)
	println('Map from array (index to element): ${map_from_arr}')

	// 5. Merge two maps
	m2 := {
		'banana': 20
		'date':   4
	}
	merged := maps.merge(m1, m2)
	println('Merged map (m2 overwrites duplicates): ${merged}')

	// 6. Merge in place (mutates the target map)
	mut mut_map := {
		'a': 1
	}
	maps.merge_in_place(mut mut_map, {'b': 2, 'c': 3})
	println('In-place merged map: ${mut_map}')
}

Context

This example demonstrates propagating request-scoped values, cancellation signals, and timeouts across thread boundaries using the context module.

v
Run
module main

import context
import time

fn main() {
	println('=== context Module Demo ===')

	// 1. Context with Value
	// Useful for passing metadata (e.g. Request ID) through call chains
	mut ctx_bg := context.background()
	mut ctx_val := context.with_value(ctx_bg, 'request_id', 'REQ-101')

	if req_id := ctx_val.value('request_id') {
		if req_id is string {
			println('Request ID in context: ${req_id}')
		}
	}

	// 2. Context with Cancellation
	mut ctx_cancel, cancel := context.with_cancel(mut ctx_val)

	// Check if canceled
	println('Before cancel - Done channel is open')
	cancel() // trigger cancellation

	// Select block to read from done channel
	done_ch := ctx_cancel.done()
	select {
		_ := <-done_ch {
			println('Context cancellation detected successfully!')
		}
		1 * time.second {
			println('Timeout waiting for cancellation.')
		}
	}

	// 3. Context with Timeout
	// Abandon execution after a duration
	mut ctx_timeout, cancel_timeout := context.with_timeout(mut ctx_bg, 50 * time.millisecond)
	defer {
		cancel_timeout()
	}

	println('Waiting for context timeout (50ms)...')
	start := time.now()
	timeout_ch := ctx_timeout.done()
	select {
		_ := <-timeout_ch {
			elapsed := time.since(start)
			println('Timeout triggered after ${elapsed.milliseconds()} ms!')
		}
		1 * time.second {
			println('Error: Timeout did not trigger in time.')
		}
	}
}

Archive Tar

This example demonstrates reading and inspecting the contents of .tar.gz files using the archive.tar module.

v
Run
module main

import archive.tar
import os

// CustomReader implements the tar.Reader interface
struct CustomReader {
pub mut:
	files_found int
}

fn (mut cr CustomReader) dir_block(mut read tar.Read, size u64) {
	println('Directory in tar: ${read.get_path()}')
}

fn (mut cr CustomReader) file_block(mut read tar.Read, size u64) {
	println('File in tar:      ${read.get_path()} (${size} bytes)')
	cr.files_found++
}

fn (mut cr CustomReader) data_block(mut read tar.Read, data []u8, pending int) {
	// Trim content bytes to display them cleanly
	content := data.bytestr().trim_space()
	println('  Content snippet: "${content}"')
}

fn (mut cr CustomReader) other_block(mut read tar.Read, details string) {
	// Ignore details for this demo
}

fn main() {
	println('=== archive.tar Module Demo ===')

	// 1. Create a dummy file to archive
	temp_file := 'temp_file_for_tar.txt'
	os.write_file(temp_file, 'Hello standard archive tar from Vlang!') or {
		println('Failed to write temp file: ${err}')
		return
	}
	defer {
		os.rm(temp_file) or {}
	}

	// 2. Create the tar.gz archive using system tar
	tar_archive := 'temp_archive.tar.gz'
	println('Creating tar archive using system tar...')
	tar_cmd := if os.user_os() == 'macos' { 'COPYFILE_DISABLE=1 tar -czf' } else { 'tar -czf' }
	os.execute('${tar_cmd} ${tar_archive} ${temp_file}')
	defer {
		os.rm(tar_archive) or {}
	}

	// 3. Read and parse the tar.gz archive using V's archive.tar module
	println('Reading archive using vlib/archive/tar:')
	mut reader := CustomReader{}

	// Read and parse
	tar.read_tar_gz_file(tar_archive, reader) or {
		println('Failed to read tar archive: ${err}')
		return
	}

	println('Total files found in archive: ${reader.files_found}')
}

Compress Deflate

This example demonstrates standard Deflate byte stream compression and decompression using the compress.deflate module.

v
Run
module main

import compress.deflate

fn main() {
	println('=== compress.deflate Module Demo ===')

	// 1. Data to compress
	original_text := 'V programming language standard library deflate compression demo. Deflate is a lossless data compression algorithm.'
	println('Original Text length: ${original_text.len} bytes')

	// 2. Compress the data using deflate.compress
	compressed_bytes := deflate.compress(original_text.bytes()) or {
		println('Compression failed: ${err}')
		return
	}
	println('Compressed size:      ${compressed_bytes.len} bytes')

	// 3. Decompress the data using deflate.decompress
	decompressed_bytes := deflate.decompress(compressed_bytes) or {
		println('Decompression failed: ${err}')
		return
	}
	println('Decompressed size:    ${decompressed_bytes.len} bytes')

	// 4. Verify the result
	decompressed_text := decompressed_bytes.bytestr()
	println('Decompressed text equals original? -> ${decompressed_text == original_text}')
	println('Decompressed Text: "${decompressed_text}"')
}

Compress Gzip

This example demonstrates compressing and decompressing binary or text data using the compress.gzip module.

v
Run
module main

import compress.gzip

fn main() {
	println('=== compress.gzip Module Demo ===')

	// 1. Original text data
	original_text := 'V programming language standard library gzip compression and decompression demonstration. This string is long enough to show compression.'
	println('Original Text length: ${original_text.len} bytes')

	// 2. Compress the data
	compressed_bytes := gzip.compress(original_text.bytes()) or {
		println('Compression failed: ${err}')
		return
	}
	println('Compressed size:      ${compressed_bytes.len} bytes')

	// 3. Decompress the data
	decompressed_bytes := gzip.decompress(compressed_bytes) or {
		println('Decompression failed: ${err}')
		return
	}
	println('Decompressed size:    ${decompressed_bytes.len} bytes')

	// 4. Convert back to string and verify
	decompressed_text := decompressed_bytes.bytestr()
	println('Decompressed text equals original? -> ${decompressed_text == original_text}')
	println('Decompressed Text: "${decompressed_text}"')
}

Compress Szip

This example demonstrates packaging multiple files into zip archives recursively, inspecting zip contents/meta-data (size, CRC32), and extracting zip files to folders using the compress.szip module.

v
Run
module main

import os
import compress.szip

fn main() {
	println('=== compress.szip Module Demo ===')

	zip_filename := 'demo_archive.zip'
	dest_dir := 'extracted_demo'

	// Ensure cleanup of any temp files/folders
	defer {
		os.rm(zip_filename) or {}
		os.rmdir_all(dest_dir) or {}
		println('\nCleaned up archive and extraction folder.')
	}

	println('\n--- 1. Creating a Zip Archive ---')
	// Open a new zip archive for writing (creating it)
	mut archive := szip.open(zip_filename, .default_compression, .write) or {
		println('Failed to create zip: ${err}')
		return
	}

	// Add first file entry
	archive.open_entry('first_file.txt') or {
		println('Failed to open entry: ${err}')
		return
	}
	archive.write_entry('Hello from the first file inside our zip archive!'.bytes()) or {
		println('Failed to write entry: ${err}')
		return
	}
	archive.close_entry()

	// Add second file entry
	archive.open_entry('docs/second_file.txt') or {
		println('Failed to open entry: ${err}')
		return
	}
	archive.write_entry('This is a second file nested inside a docs directory.'.bytes()) or {
		println('Failed to write entry: ${err}')
		return
	}
	archive.close_entry()

	// Close the zip file
	archive.close()
	println('Successfully created zip archive "${zip_filename}" with 2 entries.')

	println('\n--- 2. Inspecting the Zip Archive ---')
	// Open zip file in read-only mode to inspect its contents
	mut reader := szip.open(zip_filename, .default_compression, .read_only) or {
		println('Failed to open zip for reading: ${err}')
		return
	}

	total_entries := reader.total() or { 0 }
	println('Total entries found in zip: ${total_entries}')

	// Inspect first entry details
	reader.open_entry_by_index(0) or {
		println('Failed to open entry 0: ${err}')
		return
	}
	name := reader.name()
	size := reader.size()
	crc := reader.crc32()
	println('Entry 0 details -> Name: "${name}", Size: ${size} bytes, CRC32: ${crc}')
	reader.close_entry()
	reader.close()

	println('\n--- 3. Extracting Zip Archive contents to Directory ---')
	os.mkdir(dest_dir) or {
		println('Failed to create destination directory: ${err}')
		return
	}

	// Extract the full archive to the target folder
	success := szip.extract_zip_to_dir(zip_filename, dest_dir) or {
		println('Extraction failed: ${err}')
		return
	}

	if success {
		println('Successfully extracted all entries to folder "${dest_dir}".')
		// Read and display content from extracted files
		file1_content := os.read_file(os.join_path(dest_dir, 'first_file.txt')) or { '' }
		file2_content := os.read_file(os.join_path(dest_dir, 'docs', 'second_file.txt')) or { '' }
		println('Extracted first_file.txt: "${file1_content}"')
		println('Extracted docs/second_file.txt: "${file2_content}"')
	} else {
		println('Extraction reported failure.')
	}
}

Compress Zlib

This example demonstrates standard Zlib byte stream compression and decompression using the compress.zlib module.

v
Run
module main

import compress.zlib

fn main() {
	println('=== compress.zlib Module Demo ===')

	// 1. Data to compress
	original_text := 'V programming language standard library zlib compression demo. Zlib uses the deflate algorithm with headers and checksum.'
	println('Original Text length: ${original_text.len} bytes')

	// 2. Compress the data using zlib.compress
	compressed_bytes := zlib.compress(original_text.bytes()) or {
		println('Compression failed: ${err}')
		return
	}
	println('Compressed size:      ${compressed_bytes.len} bytes')

	// 3. Decompress the data using zlib.decompress
	decompressed_bytes := zlib.decompress(compressed_bytes) or {
		println('Decompression failed: ${err}')
		return
	}
	println('Decompressed size:    ${decompressed_bytes.len} bytes')

	// 4. Verify the result
	decompressed_text := decompressed_bytes.bytestr()
	println('Decompressed text equals original? -> ${decompressed_text == original_text}')
	println('Decompressed Text: "${decompressed_text}"')
}

Compress Zstd

This example demonstrates Zstd compression and decompression using the fast Facebook Zstandard algorithm in the compress.zstd module.

v
Run
module main

import compress.zstd

fn main() {
	println('=== compress.zstd Module Demo ===')

	// 1. Check version details
	version := zstd.version_string()
	println('ZSTD Library Version: ${version}')

	// 2. Data to compress
	original_text := 'Zstd, short for Zstandard, is a fast lossless compression algorithm developed by Facebook. It offers high compression ratios.'
	println('\nOriginal Text length: ${original_text.len} bytes')

	// 3. Compress using zstd.compress (specifying standard parameters)
	compressed_bytes := zstd.compress(original_text.bytes(), compression_level: 3) or {
		println('Compression failed: ${err}')
		return
	}
	println('Compressed size:      ${compressed_bytes.len} bytes')

	// 4. Decompress using zstd.decompress
	decompressed_bytes := zstd.decompress(compressed_bytes) or {
		println('Decompression failed: ${err}')
		return
	}
	println('Decompressed size:    ${decompressed_bytes.len} bytes')

	// 5. Verify and display result
	decompressed_text := decompressed_bytes.bytestr()
	println('Decompressed text equals original? -> ${decompressed_text == original_text}')
	println('Decompressed Text: "${decompressed_text}"')
}

Io Fs

This example demonstrates how os.File implements io.Reader and io.Writer interfaces, allowing standard file operations to utilize stream-oriented utilities like io.cp and io.BufferedReader.

v
Run
module main

import os
import io

fn main() {
	println('=== io & File System (os.File) Demo ===')

	src_path := 'temp_src_file.txt'
	dst_path := 'temp_dst_file.txt'

	// Ensure files are cleaned up on completion
	defer {
		os.rm(src_path) or {}
		os.rm(dst_path) or {}
		println('\nCleaned up temporary files.')
	}

	println('\n--- 1. Creating and Writing to File via io.Writer ---')
	// os.create returns an os.File struct, which implements the io.Writer interface.
	mut src_file := os.create(src_path) or {
		println('Failed to create file: ${err}')
		return
	}

	// Write data using the io.Writer write() method
	content_to_write := 'Hello! This is a file system demo.\nIt demonstrates how os.File integrates with the io module.\n'
	written_bytes := src_file.write(content_to_write.bytes()) or {
		println('Failed to write to file: ${err}')
		return
	}
	println('Wrote ${written_bytes} bytes to "${src_path}" using the io.Writer interface.')
	src_file.close()

	println('\n--- 2. Reading from File via io.BufferedReader ---')
	// os.open opens a file for reading, returning an os.File (which implements io.Reader).
	mut read_file := os.open(src_path) or {
		println('Failed to open file: ${err}')
		return
	}

	// Wrap os.File in io.BufferedReader for convenient line-by-line reading
	mut buf_reader := io.new_buffered_reader(reader: read_file)

	// Read lines until EOF
	for {
		line := buf_reader.read_line() or {
			break
		}
		println('Buffered Read Line: "${line}"')
	}
	read_file.close()

	println('\n--- 3. Copying File Contents using io.cp ---')
	// Re-open source file for reading (implements io.Reader)
	mut src_to_copy := os.open(src_path) or {
		println('Failed to open source file: ${err}')
		return
	}
	defer { src_to_copy.close() }

	// Create a new destination file for writing (implements io.Writer)
	mut dst_file := os.create(dst_path) or {
		println('Failed to create destination file: ${err}')
		return
	}
	defer { dst_file.close() }

	// Copy all contents from reader to writer using io.cp
	io.cp(mut src_to_copy, mut dst_file) or {
		println('Failed to copy file content: ${err}')
		return
	}
	// Explicitly close files so data is flushed and readable
	src_to_copy.close()
	dst_file.close()
	println('Copied content from "${src_path}" to "${dst_path}" via io.cp.')

	// Verify the destination file contents
	copied_content := os.read_file(dst_path) or {
		println('Failed to read destination file: ${err}')
		return
	}
	println('Copied File Contents:\n${copied_content.trim_space()}')
}

Io

This example demonstrates implementing custom Reader and Writer structs and using the io.cp utility to copy data between streams using the io module.

v
Run
module main

import io

// SimpleReader implements the io.Reader interface
struct SimpleReader {
	data string
mut:
	pos int
}

fn (mut sr SimpleReader) read(mut buf []u8) !int {
	if sr.pos >= sr.data.len {
		// Return io.Eof when the end of the stream is reached
		return io.Eof{}
	}
	mut bytes_read := 0
	for sr.pos < sr.data.len && bytes_read < buf.len {
		buf[bytes_read] = sr.data[sr.pos]
		sr.pos++
		bytes_read++
	}
	return bytes_read
}

// SimpleWriter implements the io.Writer interface
struct SimpleWriter {
mut:
	buf []u8
}

fn (mut sw SimpleWriter) write(buf []u8) !int {
	sw.buf << buf
	return buf.len
}

fn main() {
	println('=== io Module Demo ===')

	// 1. Initialize Reader and Writer
	mut reader := SimpleReader{
		data: 'Vlang standard library: io package demo.'
	}
	mut writer := SimpleWriter{}

	// 2. Use io.cp to copy data from Reader to Writer
	println('Copying data from custom Reader to custom Writer via io.cp...')
	io.cp(mut reader, mut writer) or {
		println('Error copying data: ${err}')
		return
	}

	// 3. Print the written data
	written_str := writer.buf.bytestr()
	println('Writer received: "${written_str}"')
}

Io Util

This example demonstrates using the io.util module for creating, writing to, reading from, and cleaning up temporary files and directories.

v
Run
module main

import os
import io.util

fn main() {
	println('=== io.util Module Demo ===')

	println('\n--- 1. Creating a Temporary File ---')
	// Create a temporary file. The '*' character in the pattern is replaced by a random number.
	// tfo.path defaults to the system temp directory if not specified.
	mut temp_file, temp_file_path := util.temp_file(pattern: 'v_guide_demo_*.txt') or {
		println('Failed to create temp file: ${err}')
		return
	}
	// Close the returned file handle immediately so we can open it with a clean write mode
	temp_file.close()

	defer {
		os.rm(temp_file_path) or {}
		println('Cleaned up temporary file: ${temp_file_path}')
	}
	println('Created temporary file at: ${temp_file_path}')

	// Reopen the temp file for writing explicitly
	mut f := os.open_file(temp_file_path, 'w') or {
		println('Failed to open temp file for writing: ${err}')
		return
	}

	// Write content into the temporary file
	f.write('This is some temporary data written to a temp file.'.bytes()) or {
		println('Failed to write to temp file: ${err}')
		f.close()
		return
	}
	f.close() // Close file to flush buffer and allow reading

	// Read content to verify
	content := os.read_file(temp_file_path) or {
		println('Failed to read temp file: ${err}')
		return
	}
	println('Temp file content: "${content}"')

	println('\n--- 2. Creating a Temporary Directory ---')
	// Create a temporary directory using a pattern
	temp_dir_path := util.temp_dir(pattern: 'v_guide_dir_*') or {
		println('Failed to create temp directory: ${err}')
		return
	}

	// Register cleanup on function exit
	defer {
		os.rmdir_all(temp_dir_path) or {}
		println('Cleaned up temporary directory: ${temp_dir_path}')
	}
	println('Created temporary directory at: ${temp_dir_path}')

	// Create a sub-file inside the temporary directory
	sub_file_path := os.join_path(temp_dir_path, 'sub_file.txt')
	os.write_file(sub_file_path, 'Data saved inside temporary directory.') or {
		println('Failed to create sub-file: ${err}')
		return
	}
	println('Created sub-file: ${sub_file_path}')

	// List files in the temporary directory to verify
	dir_files := os.ls(temp_dir_path) or {
		println('Failed to list temp directory: ${err}')
		return
	}
	println('Temporary directory contents: ${dir_files}')
}

Hash

This example demonstrates calculating FNV-1a (32-bit and 64-bit) hashes and CRC32 checksums using the hash module.

v
Run
module main

import hash.fnv1a
import hash.crc32

fn main() {
	println('=== hash Module Demo ===')

	input := 'V language standard library'
	println('Input string: "${input}"')

	// 1. FNV-1a 32-bit and 64-bit string hashing
	fnv_32 := fnv1a.sum32_string(input)
	fnv_64 := fnv1a.sum64_string(input)
	println('FNV-1a 32-bit hash: ${fnv_32}')
	println('FNV-1a 64-bit hash: ${fnv_64}')

	// 2. CRC32 IEEE checksum
	crc_val := crc32.sum(input.bytes())
	println('CRC32 checksum:     ${crc_val}')
}

Bitfield

This example demonstrates creating bitfields, getting/setting individual bits, performing logical operations (AND, OR, XOR, NOT), and converting to string representation using the bitfield module.

v
Run
module main

import bitfield

fn main() {
	println('=== bitfield Module Demo ===')

	// 1. Create bitfield from string
	mut bf1 := bitfield.from_str('101100')
	println('BitField 1 (from str):  ${bf1.str()}')
	println('Size of BitField 1:      ${bf1.get_size()}')
	println('Number of 1s (pop_count): ${bf1.pop_count()}')

	// 2. Accessing and modifying individual bits
	println('\nModifying individual bits:')
	println('  Bit at index 1 before:  ${bf1.get_bit(1)}')
	bf1.set_bit(1)
	println('  Bit at index 1 after set: ${bf1.get_bit(1)}')
	bf1.clear_bit(0)
	println('  Bitfield after changes: ${bf1.str()}')

	// 3. Logical bitwise operations
	mut bf2 := bitfield.from_str('011010')
	println('\nLogical operations on ${bf1.str()} and ${bf2.str()}:')

	and_result := bitfield.bf_and(bf1, bf2)
	or_result  := bitfield.bf_or(bf1, bf2)
	xor_result := bitfield.bf_xor(bf1, bf2)
	not_result := bitfield.bf_not(bf1)

	println('  AND: ${and_result.str()}')
	println('  OR:  ${or_result.str()}')
	println('  XOR: ${xor_result.str()}')
	println('  NOT: ${not_result.str()}')
}

Cli

This example demonstrates building structured CLI applications with commands, subcommands, and option flags in POSIX mode using the cli module.

v
Run
module main

import cli

fn main() {
	println('=== cli Module Demo ===')

	mut app := cli.Command{
		name:        'tool'
		description: 'A sample CLI tool showing V\'s cli package.'
		version:     '1.0.0'
		posix_mode:  true
		execute:     fn (cmd cli.Command) ! {
			println('Root command execution. Use --help to see subcommands.')
		}
		commands:    [
			cli.Command{
				name:        'greet'
				description: 'Greet a user with custom options'
				posix_mode:  true
				execute:     fn (cmd cli.Command) ! {
					name := cmd.flags.get_string('name') or { 'Guest' }
					verbose := cmd.flags.get_bool('verbose') or { false }

					if verbose {
						println('Log: Initiating greeting process...')
					}
					println('Hello, ${name}!')
				}
				flags: [
					cli.Flag{
						flag:        .string
						name:        'name'
						abbrev:      'n'
						description: 'Name of person to greet'
					},
					cli.Flag{
						flag:        .bool
						name:        'verbose'
						abbrev:      'v'
						description: 'Enable verbose logging'
					},
				]
			},
		]
	}

	app.setup()

	// Test by parsing args mock
	println('\nParsing args: tool greet --name Antigravity -v')
	app.parse(['tool', 'greet', '--name', 'Antigravity', '-v'])
}

Veb

This example demonstrates building a web application with routes, starting it in a background thread, and testing requests using the modern veb web framework.

v
Run
module main

import veb
import net.http
import time

pub struct Context {
	veb.Context
}

pub struct App {
	secret_key string
}

// Route handler
pub fn (app &App) index(mut ctx Context) veb.Result {
	return ctx.text('Hello from veb web framework!')
}

fn main() {
	println('=== veb Web Framework Demo ===')

	mut app := &App{
		secret_key: 'veb_secret_key'
	}

	port := 30088

	// Run the web server in a separate thread to avoid blocking the main execution
	spawn fn [mut app, port] () {
		println('Starting veb server on port ${port}...')
		veb.run[App, Context](mut app, port)
	}()

	// Wait for the server to spin up
	time.sleep(200 * time.millisecond)

	// Make an HTTP GET request to verify the server is running and responding
	url := 'http://localhost:${port}/'
	println('Sending request to: ${url}')

	resp := http.get(url) or {
		println('HTTP request failed: ${err}')
		return
	}

	println('Response Status Code: ${resp.status_code}')
	println('Response Body:        "${resp.body}"')
	println('veb server tested successfully.')
}

Readline

This example demonstrates prompting users for text input from terminal lines in a structured manner using the readline module.

v
Run
module main

import readline

fn main() {
	println('=== readline Module Demo ===')

	mut r := readline.Readline{}
	println('Simulating readline input (feed via stdin if non-interactive):')

	// Read a line from standard input
	line := r.read_line('Enter text: ') or {
		println('Error or EOF: ${err}')
		return
	}
	println('You entered: "${line}"')
}

Runtime

This example demonstrates inspecting hardware specifications, processor cores, system endianness, and memory usage statistics using the runtime module.

v
Run
module main

import runtime

fn main() {
	println('=== runtime Module Demo ===')

	// 1. CPU and job info
	cpus := runtime.nr_cpus()
	jobs := runtime.nr_jobs()
	println('CPU Cores:              ${cpus}')
	println('Concurrent Jobs (VJOBS): ${jobs}')

	// 2. System architecture details
	println('Is 64-bit architecture?  ${runtime.is_64bit()}')
	println('Is 32-bit architecture?  ${runtime.is_32bit()}')
	println('Is Little Endian?        ${runtime.is_little_endian()}')
	println('Is Big Endian?           ${runtime.is_big_endian()}')

	// 3. Memory statistics
	total_mem := runtime.total_memory() or { 0 }
	free_mem := runtime.free_memory() or { 0 }
	used_mem := runtime.used_memory() or { 0 }

	// Format to megabytes
	total_mb := total_mem / (1024 * 1024)
	free_mb := free_mem / (1024 * 1024)
	used_mb := used_mem / (1024 * 1024)

	println('\nPhysical Memory info:')
	println('  Total Memory: ${total_mb} MB')
	println('  Free Memory:  ${free_mb} MB')
	println('  Used (by App): ${used_mb} MB')
}

Strings.Lorem Helper

Strings Lorem

V's standard library strings.lorem module provides a pseudo-random text generator based on a Markov chain built from embedded corpora. It produces structured text in the form of paragraphs and sentences, with options to control layouts, select specific corpora, and configure deterministic output.

This example demonstrates how to:

  1. Generate pseudo-random text with default configurations.
  2. Customize the output layout by adjusting words per sentence, sentences per paragraph, and paragraphs.
  3. Select specific corpora such as lorem (Latin), poe (Edgar Allan Poe), darwin (Charles Darwin), and bard (William Shakespeare).
  4. Run deterministic generation using an RNG seed and a custom starting phrase.

v
Run
module main

import strings.lorem

fn main() {
	println('=== V strings.lorem Standard Library Demo ===')

	// 1. Basic Generation with Default Configuration
	// By default, it will choose a random corpus, seed phrase, and RNG seed.
	println('\n--- 1. Default Lorem Ipsum Generation ---')
	default_lorem := lorem.generate(lorem.LoremCfg{})
	println(default_lorem)

	// 2. Custom Layout Configuration (Paragraphs, Sentences, Words)
	println('\n--- 2. Custom Layout Generation ---')
	custom_layout := lorem.generate(lorem.LoremCfg{
		paragraphs: 2
		sentences_per_paragraph: 3
		words_per_sentence: 6
	})
	println(custom_layout)

	// 3. Selection of Specific Embedded Corpora
	// V's strings.lorem module supports four built-in corpora:
	// - 'lorem' (Standard Latin Lorem Ipsum)
	// - 'poe' (Edgar Allan Poe's The Raven)
	// - 'darwin' (Charles Darwin's Origin of Species)
	// - 'bard' (William Shakespeare's works)
	println('\n--- 3. Specific Corpora Examples ---')

	corpora := ['lorem', 'poe', 'darwin', 'bard']
	for corpus in corpora {
		text := lorem.generate(lorem.LoremCfg{
			corpus_name: corpus
			paragraphs: 1
			sentences_per_paragraph: 2
			words_per_sentence: 8
		})
		println('Corpus [${corpus}]:')
		println(text)
		println('-'.repeat(40))
	}

	// 4. Deterministic Text Generation using RNG Seed and Custom Seed Phrases
	// Using a specific `rng_seed` guarantees that the generated pseudo-random text is deterministic
	// and identical across multiple runs. Custom `seed_text` provides a starting phrase for the Markov chain.
	println('\n--- 4. Deterministic Generation with Seed & Custom Starting Phrase ---')
	deterministic_lorem_1 := lorem.generate(lorem.LoremCfg{
		corpus_name: 'poe'
		rng_seed: 42
		seed_text: 'once upon a midnight'
		paragraphs: 1
		sentences_per_paragraph: 2
		words_per_sentence: 8
	})

	deterministic_lorem_2 := lorem.generate(lorem.LoremCfg{
		corpus_name: 'poe'
		rng_seed: 42
		seed_text: 'once upon a midnight'
		paragraphs: 1
		sentences_per_paragraph: 2
		words_per_sentence: 8
	})

	println('Run 1:')
	println(deterministic_lorem_1)
	println('\nRun 2:')
	println(deterministic_lorem_2)

	// Assert that they are exactly identical due to deterministic seeding
	assert deterministic_lorem_1 == deterministic_lorem_2
	println('\nDeterministic assertion passed! Both runs generated identical text.')
}

WebAssembly Compilation

V has first-class support for WebAssembly (WASM). There are two distinct methods for compiling and working with WebAssembly in V:

  1. Compiling V source code to WASM (using direct/native backends or Emscripten).
  2. Programmatic WASM generation (using the built-in wasm instruction-level builder).

Compiling V Source to WebAssembly

1. Native Direct Backend (`-b wasm`)

V contains a native compiler backend that bypasses C intermediate code and outputs WebAssembly binary files (.wasm) directly. This backend has zero dependencies and is extremely fast, though it is currently in active development and supports a subset of the language.

To compile a V file directly to WASM:

bash
v -b wasm -o main.wasm main.v
Executing in JavaScript/Node.js:

To run the natively compiled WASM binary, instantiate it using the standard JavaScript WebAssembly API:

javascript
const fs = require("fs");

async function run() {
  const wasmBuffer = fs.readFileSync("main.wasm");
  const { instance } = await WebAssembly.instantiate(wasmBuffer, {
    env: {
      // Import host functions here if needed
    },
  });
  // Call exported V functions from JS
  console.log(instance.exports.add(5, 10));
}
run();

2. Emscripten C Backend (`-os wasm`)

For compiling complex applications, standard library features, or C-linked dependencies to WASM, V relies on the Emscripten toolchain. V translates the V code to intermediate C, and Emscripten compiles it to highly optimized WebAssembly.

Prerequisites:

Ensure the Emscripten SDK (emsdk) is installed and active on your PATH:

bash
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
Compiling with V:
bash
v -os wasm -o main.js main.v

This produces:

  • main.wasm: The compiled WebAssembly binary.
  • main.js: Emscripten glue code to load the WASM module and map runtime environments (I/O, memory, filesystem).
Running in Node.js:
bash
node main.js

Programmatic WASM Generation

Programmatic WASM Builder

V provides a built-in wasm module in its standard library that allows developers to programmatically build WebAssembly binary (.wasm) files directly using instruction-level builder patterns. This is extremely useful for compilers, runtime engines, or dynamic code generation targeting the browser and other WebAssembly runtimes.

This example demonstrates how to:

  1. Initialize a WebAssembly module (wasm.Module).
  2. Generate exported arithmetic functions (add, sub, mul).
  3. Construct and read mutable global variables (newglobal, globalget, global_set).
  4. Build recursive control structures (a factorial fac function utilizing cif, celse, c_end, and recursive call).
  5. Compile the module down to a .wasm binary slice ([]u8) and save it to disk.
v
Run
module main

import wasm
import os

fn main() {
	println('=== V WebAssembly (wasm) Module Demo ===')

	// Initialize the WebAssembly module
	mut m := wasm.Module{}
	m.enable_debug('vlang_wasm_demo')

	// --- 1. Basic Arithmetic Functions ---
	println('\n1. Generating Arithmetic Functions (add, sub, mul)...')

	// Exported 'add' function: taking two i32 parameters, returning one i32 result
	mut add_fn := m.new_function('add', [.i32_t, .i32_t], [.i32_t])
	{
		add_fn.local_get(0)
		add_fn.local_get(1)
		add_fn.add(.i32_t)
	}
	m.commit(add_fn, true) // commit with export = true

	// Exported 'sub' function
	mut sub_fn := m.new_function('sub', [.i32_t, .i32_t], [.i32_t])
	{
		sub_fn.local_get(0)
		sub_fn.local_get(1)
		sub_fn.sub(.i32_t)
	}
	m.commit(sub_fn, true)

	// Exported 'mul' function
	mut mul_fn := m.new_function('mul', [.i32_t, .i32_t], [.i32_t])
	{
		mul_fn.local_get(0)
		mul_fn.local_get(1)
		mul_fn.mul(.i32_t)
	}
	m.commit(mul_fn, true)


	// --- 2. Global Variables ---
	println('\n2. Creating Global Variables...')
	// Global variable named '__vsp' (Stack Pointer), internal/non-exported, type i32, mutable, init value 10
	vsp := m.new_global('__vsp', false, .i32_t, true, wasm.constexpr_value(10))

	// Create a function that retrieves the global value, adds 20, stores it back, and returns the new value
	mut vsp_fn := m.new_function('update_vsp', [], [.i32_t])
	{
		vsp_fn.global_get(vsp)
		vsp_fn.i32_const(20)
		vsp_fn.add(.i32_t)
		vsp_fn.global_set(vsp)
		vsp_fn.global_get(vsp)
	}
	m.commit(vsp_fn, true)


	// --- 3. Recursive Functions (Factorial) ---
	println('\n3. Generating Recursive Function (fac)...')
	// fac(n) returns n! using i64 types
	mut fac_fn := m.new_function('fac', [.i64_t], [.i64_t])
	{
		fac_fn.local_get(0)
		fac_fn.eqz(.i64_t)

		// If block: if n == 0, return 1
		ifs := fac_fn.c_if([], [.i64_t])
		{
			fac_fn.i64_const(1)
		}
		fac_fn.c_else(ifs)
		{
			// Else: return n * fac(n - 1)
			fac_fn.local_get(0) // push n

			fac_fn.local_get(0)
			fac_fn.i64_const(1)
			fac_fn.sub(.i64_t)   // n - 1
			fac_fn.call('fac')   // recursive call to fac(n - 1)

			fac_fn.mul(.i64_t)   // n * fac(n - 1)
		}
		fac_fn.c_end(ifs)
	}
	m.commit(fac_fn, true)


	// --- 4. Compilation & Output ---
	println('\n4. Compiling Module to WebAssembly Binary...')
	binary_code := m.compile()
	println('Compilation Successful! Binary size: ${binary_code.len} bytes')

	// Save compiled binary as output.wasm in the current directory
	dir := os.dir(@FILE)
	output_path := os.join_path(dir, 'output.wasm')
	os.write_file(output_path, binary_code.bytestr()) or {
		println('Failed to write output.wasm: ${err}')
		return
	}
	println('Saved Wasm binary to: ${output_path}')
}

End of Tutorial

Congratulations! You have completed the comprehensive V Programming tutorial.