V Interview Questions with Answers
Most Asked V Interview Questions for Software Engineer Roles
Introduction
This page provides a complete collection of V Interview Questions and Answers designed for software engineers, system programmers, backend developers, and candidates preparing for technical interviews. V is a statically typed, compiled programming language created by Alexander Medvednikov. It focuses on simplicity, performance, and safety, offering features like fast compilation, zero-cost C interoperability, built-in concurrency, and memory management without garbage collection. This interview guide covers beginner, intermediate, and advanced V concepts including syntax, variables, functions, structs, interfaces, generics, error handling, concurrency (spawn, channels), modules, testing, web development, and real-world programming scenarios.
Why V?
- Simplicity – clean syntax inspired by Go and Rust, easy to learn
- Performance – compiles to native code with zero-cost C interop
- Memory safety – no null, no global state, and automatic memory management
- Fast compilation – compiles millions of lines of code in seconds
- Built-in concurrency – lightweight threads (spawn) and channels for safe parallelism
- Growing ecosystem – active community, web frameworks (Vweb), and game libraries
- Used in production – companies and projects adopt V for systems, web, and CLI tools
Most Asked V Interview Questions
V (also known as Vlang) is a statically typed, compiled programming language designed for simplicity, speed, and safety. It's a new language inspired by Go and Rust.
- Fast compilation: Compiles to machine code quickly
- Memory safe: No null, no undefined behavior
- Concurrency: Built-in channels and coroutines
- Simple syntax: Easy to learn and read
- Cross-platform: Windows, Linux, macOS
// Hello World in V (Vlang)
fn main() {
println("Hello, World!")
}Variables in V are immutable by default. Use := for type inference and mut for mutable variables.
- Immutable:
name := "Alice" - Mutable:
mut age := 25 - Explicit type:
var x int = 10 - Constants:
const PI = 3.14159 - Shadowing: Variables cannot be shadowed
// Variables in V (Vlang)
fn main() {
// Immutable variable (default)
immutable_var := "World"
// Mutable variable
mut mutable_var := "Hello"
mutable_var = "V"
// Type inference
inferred := 42
// Explicit type
explicit int = 10
// Constants
const pi = 3.14159
// Display
println(immutable_var)
println(mutable_var)
println(inferred)
println(explicit)
println(pi)
}V provides a rich set of primitive and composite types.
- Integers:
int,i8,i16,i32,i64,u8,u16,u32,u64 - Floats:
f32,f64 - Boolean:
bool - String:
string - Rune:
rune - Array:
[T] - Map:
map[string]T - Struct:
struct { ... } - Interface:
interface { ... }
// Data Types in V (Vlang)
fn main() {
// Integer types
int_num := 10
unsigned := u32(100)
small_int := i8(127)
large_int := i64(1000000)
// Floating point
float_num := 3.14
double_num := 3.14159
// Boolean
is_active := true
is_inactive := false
// Character
char_val := `A`
// String
str_val := "Hello V"
// Array
arr := [1, 2, 3, 4, 5]
// Tuple
tuple := (10, 3.14, "hello")
// Map
map := {
"name": "Alice",
"age": 25
}
// Option type (null safety)
optional := ?int(nil)
// Type checking
println(typeof(int_num).name)
}Functions are defined with the fn keyword. They support parameters, return types, recursion, and multiple return values.
- Syntax:
fn name(params) return_type { ... } - Single‑expression:
fn add(a, b int) int { a + b } - Recursive:
fn factorial(n int) int { ... } - Multiple returns:
(int, int) - Higher‑order: functions as parameters
- Anonymous:
fn() { ... }()
// Functions in V (Vlang)
// Basic function
fn add(a int, b int) int {
return a + b
}
// Function with multiple return values
fn divide(a int, b int) (int, int) {
return a / b, a % b
}
// Function with default parameters
fn greet(name string) string {
if name == "" {
return "Hello, Guest!"
}
return "Hello, " + name + "!"
}
// Higher-order function
fn operate(a int, b int, op fn(int, int) int) int {
return op(a, b)
}
// Closure (lambda)
multiply := fn(a int, b int) int {
return a * b
}
// Main function
fn main() {
println(add(5, 3))
quotient, remainder := divide(10, 3)
println("Quotient: ${quotient}, Remainder: ${remainder}")
println(greet("Alice"))
println(operate(6, 7, multiply))
}Arrays are ordered, homogeneous collections. They are mutable and have a dynamic size.
- Creation:
[1, 2, 3] - Append:
arr << 4 - Insert:
arr.insert(index, value) - Delete:
arr.delete(index) - Slice:
arr[1..3] - Fixed size:
[3]int{1,2,3}
// Arrays in V (Vlang)
fn main() {
// Array creation
mut numbers := [1, 2, 3, 4, 5]
strings := ["Apple", "Banana", "Orange"]
// Access and modify
println(numbers[2])
numbers[2] = 10
// Array operations
println(numbers.len)
numbers << 6 // Append
numbers.pop() // Remove last
// Iteration
for num in numbers {
println(num)
}
// Array methods
doubled := numbers.map(fn (x int) int { return x * 2 })
filtered := numbers.filter(fn (x int) bool { return x > 2 })
sum := numbers.reduce(fn (a int, b int) int { return a + b }, 0)
println(doubled)
println(filtered)
println(sum)
// Fixed-size array
fixed_array := [5]int{1, 2, 3, 4, 5}
}Maps are associative arrays (dictionaries) that map keys to values.
- Creation:
map[string]int{ "a": 1, "b": 2 } - Access:
m["key"] or { default } - Add/Update:
m["key"] = value - Delete:
m.delete("key") - Membership:
if "key" in m { ... }
// Collections in V (Vlang)
fn main() {
// Array (list)
immutable_array := [1, 2, 3, 4, 5]
mut mutable_array := [1, 2, 3]
mutable_array << 4
mutable_array.delete(1)
// Map (dictionary)
mut map := map[string]string{}
map["key1"] = "value1"
map["key2"] = "value2"
map.delete("key1")
// Set (using map)
mut set := map[int]bool{}
set[1] = true
set[2] = true
set[3] = true
set.delete(2)
// Collection operations
numbers := [1, 2, 3, 4, 5, 6]
evens := numbers.filter(fn (x int) bool { return x % 2 == 0 })
doubled := numbers.map(fn (x int) int { return x * 2 })
sum := numbers.reduce(fn (a int, b int) int { return a + b }, 0)
println(evens)
println(doubled)
println(sum)
}Structs group related fields. They can have methods (including mutating methods).
- Definition:
struct Person { name string; age int } - Instantiation:
p := Person{"Alice", 25} - Method:
fn (p Person) greet() string { ... } - Mutating method:
fn (mut p Person) birthday() { p.age++ } - Embedding:
struct Employee { Person; salary int }
// Structs (Data Classes) in V (Vlang)
struct Person {
name string
age int
city string = "Unknown"
}
// Methods
fn (p Person) greet() string {
return "Hello, my name is " + p.name
}
// Mutating method
fn (mut p Person) increment_age() {
p.age++
}
// Constructor
fn new_person(name string, age int) Person {
return Person{
name: name,
age: age
}
}
fn main() {
mut person1 := Person{
name: "Alice",
age: 25,
city: "NYC"
}
person2 := Person{
name: "Bob",
age: 30
}
// Copy (structs are value types)
person3 := person1
person3.age = 26
println(person1.name)
println(person1.age)
println(person1.city)
println(person1.greet())
person1.increment_age()
println(person1.age)
}Interfaces define a set of methods. A type implements an interface if it has all the required methods.
- Definition:
interface Speaker { speak() string } - Implementation: implicit (no explicit
implements) - Usage: any type that provides the methods can be used as the interface
- Empty interface:
interface{}(like Go'sany) - Type assertion:
if val is Speaker { ... }
// Enums and Sum Types in V (Vlang)
enum Color {
red
green
blue
}
enum Status {
success(int)
error(string)
loading
}
// Sum type (sealed class)
type Shape = Circle | Rectangle | Point
struct Circle {
radius f64
}
struct Rectangle {
width f64
height f64
}
struct Point {}
fn (c Circle) area() f64 {
return 3.14159 * c.radius * c.radius
}
fn (r Rectangle) area() f64 {
return r.width * r.height
}
fn (p Point) area() f64 {
return 0.0
}
fn main() {
color := Color.red
status := Status.success(200)
shape := Circle{radius: 5.0}
// Match statement
match color {
Color.red { println("Color is Red") }
Color.green { println("Color is Green") }
Color.blue { println("Color is Blue") }
}
match status {
Status.success(code) { println("Success with code: ${code}") }
Status.error(msg) { println("Error: ${msg}") }
Status.loading { println("Loading...") }
}
println(shape.area())
}V uses ?T (optionals) and or blocks for error handling. There are no exceptions.
- Optional type:
?int(may have a value or benone) - Returning:
return noneorreturn value - Handling:
result := fn() or { ... } - Unwrap:
value := optional or { default } - Propagation:
value := optional?(panics on error)
// Null Safety in V (Vlang)
// Option type for null safety
struct User {
name string
email ?string // Optional field
}
fn main() {
// Optional variable
maybe_string := ?string(nil)
// maybe_string := ?string("Hello")
// Safe access with or block
value := maybe_string or { "default" }
println(value)
// Optional chaining
user := User{
name: "Alice",
email: "alice@example.com"
}
email := user.email or { "No email" }
println(email)
// If let pattern
if name := maybe_string {
println("String is: ")
}
// Guard statement
data := ?int(42)
if data == none {
println("Data is none")
} else {
println("Data: ${data}")
}
// Option with .? operator (unsafe unwrap)
// value := maybe_string.?
}Option types (?T) represent values that may be absent. They are like Maybe or Option in other languages.
- Declaration:
mut maybe := ?int = none - Setting:
maybe = 42 - Checking:
if n := maybe { ... } - Unwrapping:
value := maybe or { 0 } - Panic on none:
value := maybe?
// Control Flow in V (Vlang)
fn main() {
// If-else
age := 25
status := if age < 18 { "Minor" } else { "Adult" }
println(status)
// If-else-if
grade := 'A'
result := if grade == 'A' {
"Excellent"
} else if grade == 'B' {
"Good"
} else if grade == 'C' {
"Fair"
} else {
"Needs Improvement"
}
println(result)
// Match statement
score := 85
grade2 := match score {
90...100 { "A" }
80...89 { "B" }
70...79 { "C" }
else { "F" }
}
println(grade2)
// For loop
for i := 0; i < 5; i++ {
println(i)
}
// For-in loop
items := ["A", "B", "C"]
for item in items {
println(item)
}
// While loop
mut i := 0
for i < 5 {
println(i)
i++
}
// Infinite loop with break
mut j := 0
for {
println(j)
j++
if j >= 5 {
break
}
}
}V provides if, else, match (switch), and loops.
- If-else:
if cond { ... } else { ... } - If-else if:
if cond1 { ... } else if cond2 { ... } - Match:
match value { pattern { ... } } - For:
for i in 0..n { ... } - While:
for cond { ... }
// Interfaces and Inheritance in V (Vlang)
// Interface
interface Animal {
name string
make_sound() string
}
// Struct implementing interface
struct Dog {
name string
breed string
}
fn (d Dog) make_sound() string {
return "Woof!"
}
// Struct with inheritance (embedding)
struct Cat {
Animal // Embedding
color string
}
// Interface with multiple implementations
interface Flyable {
fly() string
}
interface Swimmable {
swim() string
}
struct Duck {
name string
}
fn (d Duck) fly() string {
return "Flying"
}
fn (d Duck) swim() string {
return "Swimming"
}
fn main() {
dog := Dog{
name: "Rex",
breed: "German Shepherd"
}
cat := Cat{
name: "Whiskers",
color: "Black"
}
duck := Duck{
name: "Donald"
}
println(dog.make_sound())
println(dog.name)
println(dog.breed)
println(duck.fly())
println(duck.swim())
}V has for loops with range, while loops, and infinite loops with break.
- Range:
for i in 0..5 { ... } - Step:
for i in 0..10 step 2 { ... } - While:
for cond { ... } - Infinite:
for { ... } - Array iteration:
for val in arr { ... }
// Properties in V (Vlang)
struct Person {
mut:
_name string
_age int
_email string
}
// Getter
fn (p Person) name() string {
return p._name.to_upper()
}
// Setter with validation
fn (mut p Person) set_name(name string) {
p._name = name.trim_space()
}
fn (p Person) age() int {
return p._age
}
fn (mut p Person) set_age(age int) {
if age >= 0 {
p._age = age
}
}
// Computed property
fn (p Person) full_name() string {
return "${p._name} (Age: ${p._age})"
}
// Lazy property
struct LazyPerson {
name string
mut:
expensive_data string = ""
computed bool = false
}
fn (mut p LazyPerson) get_expensive_data() string {
if !p.computed {
println("Computing expensive data...")
p.expensive_data = "Expensive Result"
p.computed = true
}
return p.expensive_data
}
fn main() {
mut person := Person{
_name: " Alice ",
_age: 25,
_email: "alice@example.com"
}
println(person.name()) // ALICE
person.set_name("Bob")
println(person.name()) // BOB
person.set_age(26)
println(person.age())
println(person.full_name())
mut lazy := LazyPerson{
name: "Alice"
}
println(lazy.get_expensive_data())
println(lazy.get_expensive_data()) // Cached
}match is a powerful pattern‑matching construct similar to switch in other languages.
- Syntax:
match value { pattern => { ... } } - Literal patterns:
1, "hello", true - Ranges:
1..10 - Multiple patterns:
1,2,3 => { ... } - Else:
else { ... }(default)
// Static Methods in V (Vlang)
struct MyClass {
id int
}
// Static field
const tag = "MyClass"
// Static counter
mut counter := 0
// Static method (factory)
fn create() MyClass {
counter++
return MyClass{
id: counter
}
}
// Static method
fn class_method() string {
return "Class method called, counter: ${counter}"
}
// Instance method
fn (mc MyClass) instance_method() string {
return "Instance ${mc.id} method called"
}
// Singleton pattern
struct Singleton {
mut:
data []string
}
mut singleton_instance := Singleton{}
fn get_singleton() &Singleton {
return &singleton_instance
}
fn (mut s Singleton) add_data(item string) {
s.data << item
}
fn (s Singleton) get_data() []string {
return s.data
}
fn main() {
println(tag)
obj1 := create()
obj2 := create()
println(class_method())
println(obj1.instance_method())
println(obj2.instance_method())
s1 := get_singleton()
s2 := get_singleton()
s1.add_data("Hello")
println(s2.get_data()) // ["Hello"]
}Generics allow writing code that works with multiple types. They use type parameters ([T]).
- Generic struct:
struct Box[T] { value T } - Generic function:
fn identity[T](x T) T { return x } - Type constraints:
where T == int(using interfaces or union types) - Multiple type params:
[T, U] - Type inference: often automatic
// Error Handling in V (Vlang)
// Custom error type
struct InvalidAgeError {
age int
}
fn (e InvalidAgeError) msg() string {
return "Invalid age: ${e.age}"
}
// Function that returns option/result
fn divide(a int, b int) ?int {
if b == 0 {
return none
}
return a / b
}
// Function with custom error
fn validate_age(age int) ? {
if age < 0 || age > 150 {
return error("Invalid age: ${age}")
}
}
// Using with or block
fn main() {
// Option handling
result := divide(10, 2) or {
println("Error: ${err}")
return
}
println("Result: ${result}")
// Handling division by zero
result2 := divide(10, 0) or {
println("Error: ${err}")
0
}
println("Result: ${result2}")
// Custom error handling
err := validate_age(25) or {
println("Error: ${err}")
return
}
println("Age is valid")
// Using if
if result := divide(10, 2) {
println("Result: ${result}")
} else {
println("Error occurred")
}
// Defer (finally equivalent)
defer {
println("Cleaning up...")
}
println("Processing...")
}Strings are immutable, UTF‑8 encoded bytes. They support concatenation, indexing, slicing, and many built‑in functions.
- Concatenation:
"Hello" + " " + "World" - Interpolation:
"Hello $name" - Length:
s.len(returns bytes, not runes) - Runes:
s.runes()for Unicode support - Slicing:
s[1..3] - Replace:
s.replace("old", "new")
// Closures in V (Vlang)
fn main() {
// Basic closure
square := fn (x int) int {
return x * x
}
// Closure with multiple parameters
add := fn (a int, b int) int {
return a + b
}
// Closure with multiple lines
complex := fn (x int) int {
y := x * 2
return y + 10
}
// Higher-order function
operate := fn (a int, b int, op fn(int, int) int) int {
return op(a, b)
}
// Closure capturing environment
factor := 2
multiply := fn [factor] (x int) int {
return x * factor
}
// Closure returning closure
get_multiplier := fn (factor int) fn(int) int {
return fn [factor] (x int) int {
return x * factor
}
}
// Using closures
println(square(5))
println(add(5, 3))
println(complex(5))
println(operate(6, 7, fn (a int, b int) int { return a * b }))
println(multiply(5))
double := get_multiplier(2)
println(double(5))
// Closures with arrays
numbers := [1, 2, 3, 4, 5]
doubled := numbers.map(fn (x int) int { return x * 2 })
filtered := numbers.filter(fn (x int) bool { return x > 2 })
println(doubled)
println(filtered)
}Modules are a way to organize code into reusable units. Each file starts with module name.
- Definition:
module mymodule - Export: use
pubto make functions/types visible - Import:
import mymodule - Module path: relative to
v.modfile - Standard library:
os,time,net, etc.
// Scope Functions in V (Vlang)
// V doesn't have built-in scope functions like Kotlin
// But we can use closures and patterns
struct Person {
name string
age int
city string
}
// with - execute block with object
fn with<T, R>(obj T, block fn (T) R) R {
return block(obj)
}
// also - perform additional operations
fn also<T>(obj T, block fn (T)) T {
block(obj)
return obj
}
// let - execute block
fn let<T, R>(obj T, block fn (T) R) R {
return block(obj)
}
// take-if equivalent
fn take_if<T>(value T, predicate fn (T) bool) ?T {
if predicate(value) {
return value
}
return none
}
fn main() {
person := Person{
name: "Alice",
age: 25,
city: "NYC"
}
// let
result := let(person, fn (p Person) int {
println("Name: ${p.name}")
return p.age + 1
})
println("Result: ${result}")
// with
updated := with(person, fn (mut p Person) {
p.age = 26
p.city = "SF"
})
println(updated)
// also
processed := also(person, fn (p Person) {
println("Before: ${p}")
})
println(processed)
// take-if
adult := take_if(25, fn (age int) bool { return age >= 18 }) or { 0 }
println("Adult: ${adult}")
}Use the import statement to bring in external modules.
- Standard library:
import os - Custom module:
import mymodule - Alias:
import mymodule as m - Selective import:
import os { getwd } - Only for main:
module main
// Extension Methods in V (Vlang)
// V doesn't have extension methods directly
// Using wrapper functions
// String extensions
fn is_email(s string) bool {
return s.contains("@") && s.contains(".")
}
fn add_prefix(s string, prefix string) string {
return prefix + s
}
fn word_count(s string) int {
return s.split(" ").len
}
// Numeric extensions
fn is_even(n int) bool {
return n % 2 == 0
}
fn is_odd(n int) bool {
return n % 2 != 0
}
// Array extensions
fn second_or_none<T>(arr []T) ?T {
if arr.len >= 2 {
return arr[1]
}
return none
}
fn main() {
email := "test@example.com"
println(is_email(email))
greeting := add_prefix("Hello", "Greeting: ")
println(greeting)
println(is_even(5))
println(word_count("Hello World"))
numbers := [1, 2, 3]
second := second_or_none(numbers) or { 0 }
println(second)
}The pub keyword makes functions, types, or fields publicly accessible outside the module.
- Public function:
pub fn hello() { ... } - Public struct:
pub struct Person { ... } - Public field:
pub field int - Private by default: everything is private unless marked
pub - Re‑export:
pub use(to expose items from another module)
// Type Aliases in V (Vlang)
// Type aliases for complex types
type Operation = fn(int, int) int
type UserMap = map[string]User
type UserId = int
type UserName = string
// Struct for alias example
struct User {
id UserId
name UserName
}
// Using type aliases
fn execute(op Operation, a int, b int) int {
return op(a, b)
}
// Function type alias
type OperationFn = fn(int, int) int
fn add(a int, b int) int { return a + b }
fn multiply(a int, b int) int { return a * b }
// Tuple type alias
type UserInfo = (string, int)
fn main() {
add_op := fn (a int, b int) int { return a + b }
multiply_op := fn (a int, b int) int { return a * b }
println(execute(add_op, 5, 3))
println(execute(multiply_op, 5, 3))
add_fn := add
multiply_fn := multiply
println(add_fn(5, 3))
println(multiply_fn(5, 3))
mut users := UserMap{}
users["user1"] = User{id: 1, name: "Alice"}
users["user2"] = User{id: 2, name: "Bob"}
user := users["user1"] or { User{id: 0, name: "Unknown"} }
println("User: ${user.name}")
user_info := UserInfo("Alice", 25)
println("Name: ${user_info[0]}, Age: ${user_info[1]}")
}Constants are immutable values that are known at compile time. They can be primitive types or compile‑time expressions.
- Single:
const PI = 3.14159 - Block:
const ( E = 2.718; G = 9.81 ) - Computed:
const SQUARE = 8 * 8 - Typed:
const MAX int = 100 - Scope: module‑level (cannot be inside functions)
// Inline Functions in V (Vlang)
// V doesn't have inline functions like Kotlin
// Using macros and compile-time evaluation
// Simple function (compiler may inline)
fn square(x int) int {
return x * x
}
// Generic function
fn process<T>(value T, transform fn(T) T) T {
return transform(value)
}
// Compile-time evaluation
fn measure_time(block fn()) {
start := time.now()
block()
elapsed := time.now() - start
println("Time: ${elapsed}")
}
// Macro-like function
fn measure<T>(block fn() T) T {
start := time.now()
result := block()
elapsed := time.now() - start
println("Time: ${elapsed}")
return result
}
// Usage
fn main() {
println(square(5))
result := process(5, fn (x int) int { return x * 2 })
println(result)
measure_time(fn () {
time.sleep(100 * time.millisecond)
println("Operation completed")
})
measured := measure(fn () int {
time.sleep(100 * time.millisecond)
return 42
})
println("Result: ${measured}")
}V supports pointers via the & operator and * for dereferencing. Pointers are safe by default.
- Getting address:
p := &x - Dereferencing:
*p - Modifying:
*p = 20 - Struct field access:
p.field(automatically dereferenced) - Unsafe pointers:
unsafe { ... }for low‑level operations
// Higher-Order Functions in V (Vlang)
fn main() {
// Function that takes a function as parameter
apply_operation := fn (a int, b int, op fn(int, int) int) int {
return op(a, b)
}
// Function that returns a function
get_multiplier := fn (factor int) fn(int) int {
return fn [factor] (x int) int {
return x * factor
}
}
// Function composition
compose := fn (f fn(int) int, g fn(int) int) fn(int) int {
return fn (x int) int {
return f(g(x))
}
}
// Higher-order function with multiple closures
process := fn (value int, transform fn(int) int, filter fn(int) bool) ?int {
if filter(value) {
return transform(value)
}
return none
}
// Usage
result := apply_operation(10, 20, fn (a int, b int) int { return a + b })
println(result)
double := get_multiplier(2)
println(double(5))
square := fn (x int) int { return x * x }
add_ten := fn (x int) int { return x + 10 }
square_then_add_ten := compose(add_ten, square)
println(square_then_add_ten(5))
processed := process(5, fn (x int) int { return x * 2 }, fn (x int) bool { return x > 3 }) or { 0 }
println(processed)
// Iterator higher-order functions
numbers := [1, 2, 3, 4, 5]
squared := numbers.map(fn (x int) int { return x * x })
even := numbers.filter(fn (x int) bool { return x % 2 == 0 })
sum := numbers.reduce(fn (a int, b int) int { return a + b }, 0)
println(squared)
println(even)
println(sum)
}V uses automatic memory management based on reference counting, with no GC pause. It also supports manual memory via unsafe code.
- Reference counting: automatic, deterministic
- No garbage collector: avoids pauses
- Heap allocation: via
new, arrays, maps, etc. - Manual memory:
malloc/freeinsideunsafe - Stack allocation: default for local variables
// Concurrency in V (Vlang)
import time
import sync
// Spawn a thread
fn worker(id int) {
println("Worker ${id} started")
time.sleep(500 * time.millisecond)
println("Worker ${id} finished")
}
// Channel (using shared memory)
fn producer(ch chan int) {
for i in 0..5 {
ch <- i
println("Produced: ${i}")
time.sleep(100 * time.millisecond)
}
close(ch)
}
fn consumer(ch chan int) {
for {
value, ok := <- ch
if !ok {
break
}
println("Consumed: ${value}")
time.sleep(150 * time.millisecond)
}
}
// Mutex for shared state
struct Counter {
mut:
value int
mu sync.Mutex
}
fn (mut c Counter) increment() {
c.mu.lock()
c.value++
c.mu.unlock()
}
fn main() {
// Threads
mut threads := []thread{}
for i in 0..3 {
threads << spawn worker(i)
}
for t in threads {
t.wait()
}
// Channels
ch := chan int{cap: 10}
spawn producer(ch)
spawn consumer(ch)
// Wait for channels
time.sleep(2 * time.second)
// Mutex
mut counter := Counter{}
mut threads2 := []thread{}
for _ in 0..100 {
threads2 << spawn fn (mut c Counter) {
c.increment()
}(mut counter)
}
for t in threads2 {
t.wait()
}
println("Counter: ${counter.value}")
}V provides several built‑in functions available in the global namespace.
- println/print: output
- len: length of array, string, map
- panic: halt execution with message
- assert: debug assertions
- is/as: type assertions
- dump: debug print with type information
// Iterators and Generators in V (Vlang)
// V doesn't have generators, but we can use arrays and custom iterators
// Custom iterator using array
struct Counter {
max int
mut:
current int
}
fn (mut c Counter) next() ?int {
if c.current < c.max {
c.current++
return c.current
}
return none
}
// Iterator using range
fn main() {
// Range iteration
for i in 0..10 {
println(i)
}
// Step iteration
for i in 0..10 {
if i % 2 == 0 {
println(i)
}
}
// Custom iterator
mut counter := Counter{
max: 5
}
for {
value := counter.next() or { break }
println(value)
}
// Array iteration
numbers := [1, 2, 3, 4, 5]
for num in numbers {
println(num)
}
// Map iteration
map := {
"one": 1,
"two": 2,
"three": 3
}
for key, value in map {
println("${key}: ${value}")
}
// Lazy iteration (using filter and map)
result := numbers.filter(fn (x int) bool { return x % 2 == 0 })
.map(fn (x int) int { return x * 2 })
println(result)
}File I/O is provided by the os module. Operations include reading, writing, appending, and deleting.
- Write:
os.write_file("file.txt", "content") or { ... } - Read:
content := os.read_file("file.txt") or { return } - Append:
os.append_file("file.txt", "more") - Delete:
os.rm("file.txt") - Check existence:
os.exists("file.txt")
// Channels and Communication in V (Vlang)
import time
// Select statement
fn select_example() {
ch1 := chan int{cap: 10}
ch2 := chan int{cap: 10}
spawn fn (ch chan int) {
time.sleep(500 * time.millisecond)
ch <- 42
}(ch1)
spawn fn (ch chan int) {
time.sleep(300 * time.millisecond)
ch <- 100
}(ch2)
for {
select {
value := <-ch1 {
println("Channel 1: ${value}")
}
value := <-ch2 {
println("Channel 2: ${value}")
}
timeout: time.after(1000 * time.millisecond) {
println("Timeout")
return
}
}
}
}
// Buffered channels
fn buffered_channels() {
ch := chan int{cap: 3}
ch <- 1
ch <- 2
ch <- 3
println(<-ch)
println(<-ch)
println(<-ch)
}
// Fan-out pattern
fn fan_out() {
ch := chan int{cap: 10}
spawn fn (ch chan int) {
for i in 0..10 {
ch <- i
}
close(ch)
}(ch)
for i in 0..3 {
spawn fn (id int, ch chan int) {
for value := range ch {
println("Consumer ${id}: ${value}")
time.sleep(100 * time.millisecond)
}
}(i, ch)
}
time.sleep(2 * time.second)
}
// Fan-in pattern
fn fan_in() {
ch := chan int{cap: 10}
for i in 0..3 {
spawn fn (id int, ch chan int) {
for j in 0..5 {
ch <- id * 10 + j
time.sleep(50 * time.millisecond)
}
}(i, ch)
}
for i in 0..15 {
value := <-ch
println("Received: ${value}")
}
}
fn main() {
select_example()
buffered_channels()
fan_out()
fan_in()
}Channels are used for communication between coroutines (spawn). They are typed and can be buffered.
- Creation:
ch := chan int{cap: 10} - Send:
ch <- value - Receive:
val := <-ch - Close:
close(ch) - Iteration:
for val in ch { ... }
// Enums and Pattern Matching in V (Vlang)
enum Result[T] {
success(T)
error(string)
loading
}
enum Shape {
circle(radius f64)
rectangle(width f64, height f64)
point
}
fn (s Shape) area() f64 {
return match s {
Shape.circle(radius) { 3.14159 * radius * radius }
Shape.rectangle(width, height) { width * height }
Shape.point { 0.0 }
}
}
enum Payment {
cash(amount f64)
credit_card(number string, expiry string)
paypal(email string)
}
fn main() {
result := Result[int].success(200)
shape := Shape.circle(5.0)
payment := Payment.credit_card("1234-5678", "12/25")
// Match on result
match result {
Result.success(value) { println("Success: ${value}") }
Result.error(msg) { println("Error: ${msg}") }
Result.loading { println("Loading...") }
}
// Match on shape
match shape {
Shape.circle(radius) { println("Circle with radius: ${radius}") }
Shape.rectangle(width, height) { println("Rectangle: ${width}x${height}") }
Shape.point { println("Point") }
}
// Match on payment
match payment {
Payment.cash(amount) { println("Cash amount: ${amount}") }
Payment.credit_card(number, expiry) { println("Card: ${number}, Expiry: ${expiry}") }
Payment.paypal(email) { println("PayPal: ${email}") }
}
println("Area: ${shape.area()}")
}Use the spawn keyword to run a function concurrently as a coroutine.
- Syntax:
spawn fn() { ... } - Passing arguments:
spawn worker(1) - Lightweight: coroutines are cheap
- No explicit join: use channels or time to sync
- Example:
spawn fn() { println("Hello from coroutine") }
// Generics in V (Vlang)
// Generic struct
struct Box[T] {
value T
}
fn (b Box[T]) get_value() T {
return b.value
}
// Generic function
fn swap<T>(a T, b T) (T, T) {
return b, a
}
// Generic with constraints
fn sum_numbers<T>(items []T) T {
mut sum := 0
for item in items {
sum += item
}
return sum
}
// Generic with multiple constraints
fn process<T>(value T) string {
return typeof(value).name
}
// Generic interface
interface Repository[T] {
get(id int) ?T
save(item T)
}
struct UserRepository {
mut:
users []User
}
fn (mut r UserRepository) get(id int) ?User {
for user in r.users {
if user.id == id {
return user
}
}
return none
}
fn (mut r UserRepository) save(user User) {
r.users << user
}
struct User {
id int
name string
}
// Main
fn main() {
// Generic struct
box_int := Box[int]{value: 42}
box_string := Box[string]{value: "Hello"}
println(box_int.get_value())
println(box_string.get_value())
// Generic function
a, b := swap(1, 2)
println("Swapped: ${a}, ${b}")
// Generic constraints
numbers := [1, 2, 3, 4, 5]
// println(sum_numbers(numbers))
// Generic interface
mut repo := UserRepository{}
repo.save(User{id: 1, name: "Alice"})
user := repo.get(1) or { User{id: 0, name: "Unknown"} }
println("User: ${user.name}")
}defer schedules a block to run when the surrounding function exits, regardless of how it exits.
- Syntax:
defer { ... } - Runs at end: even if panic occurs
- Multiple defers: execute in reverse order (LIFO)
- Useful for cleanup: closing files, unlocking mutexes
// Interfaces and Polymorphism in V (Vlang)
// Interface definition
interface Shape {
area() f64
perimeter() f64
}
// Circle implementation
struct Circle {
radius f64
}
fn (c Circle) area() f64 {
return 3.14159 * c.radius * c.radius
}
fn (c Circle) perimeter() f64 {
return 2 * 3.14159 * c.radius
}
// Rectangle implementation
struct Rectangle {
width f64
height f64
}
fn (r Rectangle) area() f64 {
return r.width * r.height
}
fn (r Rectangle) perimeter() f64 {
return 2 * (r.width + r.height)
}
// Triangle implementation
struct Triangle {
a f64
b f64
c f64
}
fn (t Triangle) area() f64 {
s := (t.a + t.b + t.c) / 2
return (s * (s - t.a) * (s - t.b) * (s - t.c)).sqrt()
}
fn (t Triangle) perimeter() f64 {
return t.a + t.b + t.c
}
// Polymorphic function
fn print_shape_info(s Shape) {
println("Area: ${s.area():.2f}")
println("Perimeter: ${s.perimeter():.2f}")
}
// Interface with optional methods
interface Drawable {
draw()
color() string
}
fn main() {
shapes := [
Shape(Circle{radius: 5.0}),
Shape(Rectangle{width: 4.0, height: 6.0}),
Shape(Triangle{a: 3.0, b: 4.0, c: 5.0})
]
for shape in shapes {
print_shape_info(shape)
}
}Enums define a set of named constants. They can have integer values and are used for type safety.
- Definition:
enum Color { red; green; blue } - Values:
Color.red,Color.green - Custom values:
enum Status { success = 200; error = 500 } - Conversion:
int(Color.red)andColor(int_value) - Matching:
match c { .red { ... } }
// Singleton Pattern in V (Vlang)
// Singleton using global variable
struct AppConfig {
api_url string
timeout int
}
const config = AppConfig{
api_url: "https://api.example.com",
timeout: 5000
}
// Singleton with lazy initialization
struct UserManager {
mut:
users []string
}
mut user_manager_instance := UserManager{}
fn get_user_manager() &UserManager {
return &user_manager_instance
}
// Singleton with mutex
import sync
struct Database {
mut:
connected bool
mu sync.Mutex
}
mut db_instance := &Database{
connected: false
}
fn get_database() &Database {
db_instance.mu.lock()
if !db_instance.connected {
db_instance.connected = true
println("Database connected")
}
db_instance.mu.unlock()
return db_instance
}
// Singleton using atomic
struct AtomicSingleton {
mut:
data map[string]string
}
atomic_instance := &AtomicSingleton{
data: map[string]string{}
}
fn get_atomic_singleton() &AtomicSingleton {
return atomic_instance
}
fn main() {
// Using const singleton
println(config.api_url)
println(config.timeout)
// Using lazy singleton
manager1 := get_user_manager()
manager2 := get_user_manager()
manager1.users << "Alice"
manager1.users << "Bob"
println(manager2.users) // ["Alice", "Bob"]
// Using mutex singleton
db1 := get_database()
db2 := get_database()
println(db1.connected)
println(db2.connected)
}Unions are sum types that can hold values of different types, similar to tagged unions or enums with payloads.
- Definition:
type MyUnion = int | string - Variants:
MyUnion(42)orMyUnion("hello") - Matching:
match u { int { ... } string { ... } } - Used for: error handling, AST nodes, etc.
- Safe: exhaustive match required ````
// Builder Pattern in V (Vlang)
struct User {
name string
age int
email string
city string
}
struct UserBuilder {
mut:
name string
age int
email string
city string
}
fn UserBuilder.new() UserBuilder {
return UserBuilder{
city: "Unknown"
}
}
fn (mut b UserBuilder) name(name string) &UserBuilder {
b.name = name
return b
}
fn (mut b UserBuilder) age(age int) &UserBuilder {
b.age = age
return b
}
fn (mut b UserBuilder) email(email string) &UserBuilder {
b.email = email
return b
}
fn (mut b UserBuilder) city(city string) &UserBuilder {
b.city = city
return b
}
fn (b UserBuilder) build() User {
return User{
name: b.name,
age: b.age,
email: b.email,
city: b.city
}
}
// Query builder
struct Query {
table string
fields []string
conditions []string
order []string
limit int
}
struct QueryBuilder {
mut:
table string
fields []string
conditions []string
order []string
limit int
}
fn QueryBuilder.from(table string) QueryBuilder {
return QueryBuilder{
table: table
}
}
fn (mut b QueryBuilder) select(fields ...string) &QueryBuilder {
b.fields = fields
return b
}
fn (mut b QueryBuilder) where(condition string) &QueryBuilder {
b.conditions << condition
return b
}
fn (mut b QueryBuilder) order_by(field string, asc bool) &QueryBuilder {
direction := if asc { "ASC" } else { "DESC" }
b.order << "${field} ${direction}"
return b
}
fn (mut b QueryBuilder) limit(count int) &QueryBuilder {
b.limit = count
return b
}
fn (b QueryBuilder) build() string {
mut query := "SELECT "
if b.fields.len == 0 {
query += "*"
} else {
query += b.fields.join(", ")
}
query += " FROM ${b.table}"
if b.conditions.len > 0 {
query += " WHERE " + b.conditions.join(" AND ")
}
if b.order.len > 0 {
query += " ORDER BY " + b.order.join(", ")
}
if b.limit > 0 {
query += " LIMIT ${b.limit}"
}
return query
}
fn main() {
// User builder
user := UserBuilder.new()
.name("Alice")
.age(25)
.email("alice@example.com")
.city("NYC")
.build()
println(user)
// Query builder
query := QueryBuilder.from("users")
.select("name", "age")
.where("age > 18")
.order_by("name", true)
.limit(10)
.build()
println(query)
}V has a built‑in test framework. Test files end with _test.v and contain test_* functions.
- Test function:
fn test_my_func() { assert 1 + 1 == 2 } - Run tests:
v test . - Assertions:
assert condition - Testing module:
import testingfor advanced assertions - Benchmarks:
fn benchmark_my_func(b &testing.B) { ... }
// Macros in V (Vlang)
// V has limited macro support
// Using compile-time code generation
// Simple macro (compile-time function)
fn square(x int) int {
return x * x
}
// Compile-time evaluation
const PI = 3.14159
// Using comptime for conditional compilation
fn platform_specific() {
$if windows {
println("Running on Windows")
} $else $if linux {
println("Running on Linux")
} $else {
println("Running on unknown OS")
}
}
// Generic macro for logging
fn log(message string) {
$if debug {
println("[DEBUG] ${message}")
} $else {
// No debug output
}
}
// Macro-like function for timing
fn measure<T>(block fn() T) T {
start := time.now()
result := block()
elapsed := time.now() - start
println("Time: ${elapsed}")
return result
}
// Assert macro
fn assert_equal<T>(expected T, actual T) {
if expected != actual {
println("Assertion failed: expected ${expected}, got ${actual}")
exit(1)
}
}
fn main() {
// Compile-time evaluation
println("PI: ${PI}")
// Platform-specific code
platform_specific()
// Debug logging
log("This is a debug message")
// Timing
result := measure(fn () int {
time.sleep(100 * time.millisecond)
return 42
})
println("Result: ${result}")
// Assertion
assert_equal(5, 5)
// assert_equal(5, 6) // Would fail
}V provides a simple, fast build system using the v command. It supports building, running, testing, and cross‑compilation.
- Build:
v build . - Run:
v run main.v - Cross‑compile:
v -os windows . - Production build:
v -prod . - With GC:
v -gc boehm . - Debug:
v -g .
// Reflection in V (Vlang)
// V has limited reflection support
import reflect
struct Person {
name string
age int
email string
}
fn main() {
person := Person{
name: "Alice",
age: 25,
email: "alice@example.com"
}
// Type information
println("Type: ${typeof(person).name}")
// Field iteration
for field in reflect.fields(person) {
println("${field.name}: ${field.value}")
}
// Type checking
if person is Person {
println("Is Person")
}
// Generic type handling
print_type_info(person)
}
fn print_type_info<T>(value T) {
println("Type: ${typeof(value).name}")
$if T is Person {
println("This is a Person")
p := value as Person
println("Name: ${p.name}")
} $else {
println("Unknown type")
}
}Reverse a string using runes (to handle Unicode) and manual iteration.
- Runes:
s.runes()for Unicode support - Manual: Iterate from end to start
- Return:
result.string() - Complexity: O(n) time
// Reverse a string in V (Vlang)
fn reverse_string(s string) string {
mut runes := s.runes()
mut result := []rune{}
for i := runes.len - 1; i >= 0; i-- {
result << runes[i]
}
return result.string()
}
fn main() {
println(reverse_string("hello")) // "olleh"
}Check if a string is a palindrome using two-pointer approach on runes.
- Two-pointer: Compare from both ends
- Runes:
s.runes()for Unicode support - Case sensitive: V strings are case sensitive
- Complexity: O(n) time
// Check palindrome in V (Vlang)
fn is_palindrome(s string) bool {
runes := s.runes()
mut i := 0
mut j := runes.len - 1
for i < j {
if runes[i] != runes[j] {
return false
}
i++
j--
}
return true
}
fn main() {
println(is_palindrome("racecar")) // true
println(is_palindrome("hello")) // false
}Find maximum value using manual iteration.
- Manual: Iterate and track max
- Option: Returns
?intfor empty array - Complexity: O(n) time
- Return: Max value or none
// Find max in array in V (Vlang)
fn find_max(arr []int) ?int {
if arr.len == 0 {
return none
}
mut max := arr[0]
for num in arr {
if num > max {
max = num
}
}
return max
}
fn main() {
numbers := [1, 5, 3, 9, 2]
max := find_max(numbers) or { 0 }
println(max) // 9
}Remove duplicates using map to track seen elements.
- Map:
map[int]bool - Manual: Iterate and check
- Complexity: O(n) time
- Return: Unique array
// Remove duplicates in V (Vlang)
fn remove_duplicates(arr []int) []int {
mut seen := map[int]bool{}
mut result := []int{}
for item in arr {
if !seen[item] {
seen[item] = true
result << item
}
}
return result
}
fn main() {
numbers := [1, 2, 2, 3, 3, 4]
unique := remove_duplicates(numbers)
println(unique) // [1, 2, 3, 4]
}Merge arrays using clone() and << operator.
- clone:
arr1.clone() - Append:
result << arr2 - Unique merge: Check for duplicates
- Generic:
<T>support
// Merge arrays in V (Vlang)
fn merge_arrays<T>(arr1 []T, arr2 []T) []T {
mut result := arr1.clone()
result << arr2
return result
}
fn merge_unique(arr1 []int, arr2 []int) []int {
mut result := arr1.clone()
for item in arr2 {
if !result.contains(item) {
result << item
}
}
return result
}
fn main() {
arr1 := [1, 2, 3]
arr2 := [3, 4, 5]
merged := merge_arrays(arr1, arr2)
println(merged) // [1, 2, 3, 3, 4, 5]
unique := merge_unique(arr1, arr2)
println(unique) // [1, 2, 3, 4, 5]
}Convert string to number using .int() method.
- .int():
s.int() - Option: Returns
?int - Error handling: Use
orblock - Return: Number or default
// Convert string to number in V (Vlang)
fn string_to_number(s string) ?int {
return s.int()
}
fn main() {
num := string_to_number("42") or { 0 }
println(num) // 42
}Iterate through map using for key, value in map.
- For-in:
for key, value in map - Keys:
map.keys() - Values:
map.values() - Order: Not guaranteed
// Loop through map in V (Vlang)
fn main() {
map := {
"name": "Alice",
"age": 25,
"city": "NYC"
}
for key, value in map {
println("${key} => ${value}")
}
}Delay execution using time.sleep().
- Sleep:
time.sleep(delay_ms * time.millisecond) - Blocking: Blocks current thread
- Async: Use
spawnfor non-blocking - Return: Executes function after delay
// Delay function execution in V (Vlang)
import time
fn delayed_execution(delay_ms int, fn fn()) {
time.sleep(delay_ms * time.millisecond)
fn()
}
fn main() {
delayed_execution(2000, fn () {
println("After 2 seconds")
})
}Make HTTP GET requests using net.http module.
- http.get:
http.get(url) - Option: Returns
?string - Error handling: Use
orblock - Response:
response.body
// HTTP GET request in V (Vlang)
import net.http
fn fetch_data(url string) ?string {
response := http.get(url)?
return response.body
}
fn main() {
data := fetch_data("https://api.example.com/data") or { "" }
println(data)
}Create a Deferred using channels for async communication.
- Channel:
chan string{cap: 1} - spawn: Run in background
- Send:
ch <- result - Receive:
<-ch
// Promise-like Deferred in V (Vlang)
import time
fn create_deferred(should_resolve bool) chan string {
ch := chan string{cap: 1}
spawn fn (ch chan string, resolve bool) {
time.sleep(1000 * time.millisecond)
if resolve {
ch <- "Success!"
} else {
ch <- "Failed!"
}
}(ch, should_resolve)
return ch
}
fn main() {
ch := create_deferred(true)
result := <-ch
println(result)
}Calculate factorial using recursion or iteration.
- Recursive:
n * factorial(n - 1) - Base case:
n <= 1 - Iterative: Loop with multiplication
- Return type:
int
// Factorial in V (Vlang)
fn factorial(n int) int {
if n <= 1 {
return 1
}
return n * factorial(n - 1)
}
fn main() {
println(factorial(5)) // 120
}Calculate Fibonacci using recursion, iteration, or memoization.
- Recursive:
fib(n-1) + fib(n-2) - Iterative: Loop with variables
- Memoization: Cache in map
- Complexity: O(n) with memoization
// Fibonacci in V (Vlang)
fn fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n - 1) + fibonacci(n - 2)
}
fn main() {
println(fibonacci(8)) // 21
}FizzBuzz using if-else with modulo operations.
- Modulo: Check divisibility by 3, 5, 15
- Order: Check 15 first
- Range:
for i in 1..n + 1 - Output: Print results
// FizzBuzz in V (Vlang)
fn fizzbuzz(n int) {
for i in 1..n + 1 {
if i % 15 == 0 {
println("FizzBuzz")
} else if i % 3 == 0 {
println("Fizz")
} else if i % 5 == 0 {
println("Buzz")
} else {
println(i)
}
}
}
fn main() {
fizzbuzz(15)
}Find missing number using formula n*(n+1)/2 - sum.
- Formula:
total - sum - reduce:
arr.reduce(fn (a, b) int { return a + b }, 0) - Complexity: O(n) time
- Return: Missing number
// Find missing number in V (Vlang)
fn find_missing(arr []int) int {
n := arr.len + 1
total := n * (n + 1) / 2
sum := arr.reduce(fn (a int, b int) int { return a + b }, 0)
return total - sum
}
fn main() {
numbers := [1, 2, 4, 5, 6]
missing := find_missing(numbers)
println(missing) // 3
}Find duplicates using map to track seen elements.
- Map:
map[int]bool - Track: Mark seen elements
- Complexity: O(n) time
- Return: Duplicates array
// Find duplicates in V (Vlang)
fn find_duplicates(arr []int) []int {
mut seen := map[int]bool{}
mut duplicates := []int{}
for item in arr {
if seen[item] {
duplicates << item
} else {
seen[item] = true
}
}
return duplicates
}
fn main() {
numbers := [1, 2, 3, 2, 4, 3]
dups := find_duplicates(numbers)
println(dups) // [2, 3]
}Calculate sum using reduce or manual iteration.
- reduce:
arr.reduce(fn (a, b) int { return a + b }, 0) - Manual: Loop and accumulate
- Return type:
int - Complexity: O(n) time
// Sum of array in V (Vlang)
fn sum_array(arr []int) int {
return arr.reduce(fn (a int, b int) int { return a + b }, 0)
}
fn main() {
numbers := [1, 2, 3, 4, 5]
sum := sum_array(numbers)
println(sum) // 15
}Calculate average using sum divided by length.
- Method:
sum / arr.len - Type: Use
f64for precision - Empty array: Return 0.0
- Return type:
f64
// Average of array in V (Vlang)
fn average_array(arr []f64) f64 {
if arr.len == 0 {
return 0.0
}
sum := arr.reduce(fn (a f64, b f64) f64 { return a + b }, 0.0)
return sum / arr.len
}
fn main() {
numbers := [1.0, 2.0, 3.0, 4.0, 5.0]
avg := average_array(numbers)
println(avg) // 3.0
}Sort using arr.sort() method.
- sort():
arr.sort() - In-place: Modifies original array
- Complexity: O(n log n)
- Return: Sorted array
// Sort array ascending in V (Vlang)
fn sort_ascending(mut arr []int) {
arr.sort()
}
fn main() {
mut numbers := [5, 2, 8, 1, 9]
sort_ascending(mut numbers)
println(numbers) // [1, 2, 5, 8, 9]
}Sort descending using sort() and reverse().
- sort():
arr.sort() - reverse():
arr.reverse() - In-place: Modifies original array
- Complexity: O(n log n)
// Sort array descending in V (Vlang)
fn sort_descending(mut arr []int) {
arr.sort()
arr.reverse()
}
fn main() {
mut numbers := [5, 2, 8, 1, 9]
sort_descending(mut numbers)
println(numbers) // [9, 8, 5, 2, 1]
}Flatten a 2D array by concatenating sub‑arrays.
- Loop: Iterate through sub‑arrays
- Concatenate:
result << sub - Complexity: O(n) time
- Return: Flattened array
// Flatten nested array in V (Vlang)
fn flatten_array(arr [][]int) []int {
mut result := []int{}
for sub in arr {
result << sub
}
return result
}
fn main() {
nested := [[1, 2], [3, 4], [5, 6]]
flat := flatten_array(nested)
println(flat) // [1, 2, 3, 4, 5, 6]
}Split array into chunks of given size.
- Loop: Iterate with step size
- Slice:
arr[i..i+size] - Edge case: Handle last chunk
- Return: 2D array of chunks
// Chunk array in V (Vlang)
fn chunk_array(arr []int, size int) [][]int {
mut chunks := [][]int{}
mut current := []int{}
for i, item in arr {
current << item
if (i + 1) % size == 0 {
chunks << current
current = []
}
}
if current.len > 0 {
chunks << current
}
return chunks
}
fn main() {
numbers := [1, 2, 3, 4, 5, 6]
chunks := chunk_array(numbers, 2)
println(chunks) // [[1, 2], [3, 4], [5, 6]]
}Binary search using while loop on sorted array.
- While loop:
left <= right - Mid calculation:
(left + right) / 2 - Option: Returns
?int - Complexity: O(log n) time
// Binary search in V (Vlang)
fn binary_search(arr []int, target int) ?int {
mut left := 0
mut right := arr.len - 1
for left <= right {
mid := (left + right) / 2
if arr[mid] == target {
return mid
} else if arr[mid] < target {
left = mid + 1
} else {
right = mid - 1
}
}
return none
}
fn main() {
numbers := [1, 2, 3, 4, 5, 6, 7]
index := binary_search(numbers, 5) or { -1 }
println(index) // 4
}Quick sort using recursion and partitioning.
- Algorithm: Choose pivot, partition, recurse
- Time: O(n log n) average
- Memory: Creates new arrays
- Return: Sorted array
// Quick sort in V (Vlang)
fn quick_sort(arr []int) []int {
if arr.len <= 1 {
return arr
}
pivot := arr[0]
mut left := []int{}
mut right := []int{}
for i in 1..arr.len {
if arr[i] < pivot {
left << arr[i]
} else {
right << arr[i]
}
}
return quick_sort(left) + [pivot] + quick_sort(right)
}
fn main() {
numbers := [5, 3, 8, 4, 2, 7, 1, 6]
sorted := quick_sort(numbers)
println(sorted)
}Merge sort using divide-and-conquer and merging.
- Algorithm: Divide, sort, merge
- Time: O(n log n)
- Space: O(n) auxiliary space
- Return: Sorted array
// Merge sort in V (Vlang)
fn merge_sort(arr []int) []int {
if arr.len <= 1 {
return arr
}
mid := arr.len / 2
left := merge_sort(arr[..mid])
right := merge_sort(arr[mid..])
return merge(left, right)
}
fn merge(left []int, right []int) []int {
mut result := []int{}
mut i := 0
mut j := 0
for i < left.len && j < right.len {
if left[i] <= right[j] {
result << left[i]
i++
} else {
result << right[j]
j++
}
}
result << left[i..]
result << right[j..]
return result
}
fn main() {
numbers := [5, 3, 8, 4, 2, 7, 1, 6]
sorted := merge_sort(numbers)
println(sorted)
}Bubble sort with early termination.
- Algorithm: Compare adjacent, swap
- Time: O(n²) worst case
- Optimization: Stop if no swaps
- In-place: Modifies original array
// Bubble sort in V (Vlang)
fn bubble_sort(mut arr []int) {
for i in 0..arr.len - 1 {
mut swapped := false
for j in 0..arr.len - i - 1 {
if arr[j] > arr[j + 1] {
arr[j], arr[j + 1] = arr[j + 1], arr[j]
swapped = true
}
}
if !swapped {
break
}
}
}
fn main() {
mut numbers := [5, 3, 8, 4, 2, 7, 1, 6]
bubble_sort(mut numbers)
println(numbers)
}Find common elements using map for membership.
- Map:
map[int]bool - Filter: Check if in map
- Complexity: O(n) time
- Return: Array of common elements
// Intersection of arrays in V (Vlang)
fn intersection(arr1 []int, arr2 []int) []int {
mut set := map[int]bool{}
for item in arr2 {
set[item] = true
}
mut result := []int{}
for item in arr1 {
if set[item] {
result << item
}
}
return result
}
fn main() {
arr1 := [1, 2, 3, 4]
arr2 := [3, 4, 5, 6]
inter := intersection(arr1, arr2)
println(inter) // [3, 4]
}Combine arrays with unique elements using map.
- Map:
map[int]bool - Keys:
map.keys() - Complexity: O(n) time
- Return: Array of unique elements
// Union of arrays in V (Vlang)
fn union(arr1 []int, arr2 []int) []int {
mut set := map[int]bool{}
for item in arr1 {
set[item] = true
}
for item in arr2 {
set[item] = true
}
mut result := []int{}
for key in set.keys() {
result << key
}
return result
}
fn main() {
arr1 := [1, 2, 3]
arr2 := [3, 4, 5]
uni := union(arr1, arr2)
println(uni) // [1, 2, 3, 4, 5]
}Find elements in first array not in second using map.
- Map:
map[int]bool - Filter: Check if not in map
- Complexity: O(n) time
- Return: Array of differences
// Difference of arrays in V (Vlang)
fn difference(arr1 []int, arr2 []int) []int {
mut set := map[int]bool{}
for item in arr2 {
set[item] = true
}
mut result := []int{}
for item in arr1 {
if !set[item] {
result << item
}
}
return result
}
fn main() {
arr1 := [1, 2, 3, 4]
arr2 := [3, 4, 5, 6]
diff := difference(arr1, arr2)
println(diff) // [1, 2]
}Group items by property using map.
- Map:
map[string][]Item - Loop: Iterate and group
- Complexity: O(n) time
- Return: Map of groups
// Group by property in V (Vlang)
struct Item {
type string
name string
}
fn group_by_property(items []Item, key string) map[string][]Item {
mut groups := map[string][]Item{}
for item in items {
value := if key == "type" { item.type } else { item.name }
groups[value] << item
}
return groups
}
fn main() {
data := [
Item{type: "fruit", name: "apple"},
Item{type: "fruit", name: "banana"},
Item{type: "veg", name: "carrot"}
]
groups := group_by_property(data, "type")
for key, items in groups {
println("${key}: ${items}")
}
}Deep clone by manually copying struct fields.
- Manual: Copy each field
- Nested: Copy nested structs
- Return: Independent copy
- Value types: Structs are value types
// Deep clone in V (Vlang)
struct Address {
city string
zip string
}
struct User {
name string
address Address
}
fn deep_clone(user User) User {
return User{
name: user.name,
address: Address{
city: user.address.city,
zip: user.address.zip
}
}
}
fn main() {
original := User{
name: "Alice",
address: Address{
city: "NYC",
zip: "10001"
}
}
cloned := deep_clone(original)
println(original)
println(cloned)
}Perform immutable update by creating new struct.
- New struct: Create with updated fields
- Return: New immutable object
- Original: Remains unchanged
- Value types: Structs are value types
// Immutable update in V (Vlang)
struct User {
name string
age int
}
struct State {
user User
}
fn update_age(state State, new_age int) State {
return State{
user: User{
name: state.user.name,
age: new_age
}
}
}
fn main() {
state := State{
user: User{
name: "Alice",
age: 25
}
}
new_state := update_age(state, 26)
println(state.user.age) // 25
println(new_state.user.age) // 26
}Pipe composes functions from left to right.
- Implementation: Loop through functions
- Generic:
<T>support - Return: Final result
- Direction: Left to right
// Pipe function in V (Vlang)
fn pipe<T>(value T, fns []fn(T) T) T {
mut result := value
for fn in fns {
result = fn(result)
}
return result
}
fn double(x int) int { return x * 2 }
fn add_ten(x int) int { return x + 10 }
fn square(x int) int { return x * x }
fn main() {
process := [double, add_ten, square]
result := pipe(5, process)
println(result) // (5*2+10)^2 = 400
}Compose functions from right to left.
- Implementation: Loop in reverse
- Generic:
<T>support - Return: Composed function
- Direction: Right to left
// Compose function in V (Vlang)
fn compose(fns []fn(int) int) fn(int) int {
return fn (x int) int {
mut result := x
for i := fns.len - 1; i >= 0; i-- {
result = fns[i](result)
}
return result
}
}
fn double(x int) int { return x * 2 }
fn add_ten(x int) int { return x + 10 }
fn square(x int) int { return x * x }
fn main() {
process := compose([square, add_ten, double])
result := process(5)
println(result) // (5*2+10)^2 = 400
}Cache function results based on arguments using map.
- Map:
map[T]U - Generic:
<T, U>support - Return: Cached or computed result
- Trade-off: Memory for speed
// Memoization in V (Vlang)
fn memoize<T, U>(fn fn(T) U) fn(T) U {
mut cache := map[T]U{}
return fn (arg T) U {
if arg in cache {
return cache[arg]
}
result := fn(arg)
cache[arg] = result
return result
}
}
fn fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n - 1) + fibonacci(n - 2)
}
fn main() {
fib := memoize(fibonacci)
println(fib(10)) // 55
}Ensure a function is called only once using flag.
- Flag:
mut called := false - Result: Cache the result
- Generic:
<T>support - Use case: Initialization
// Once function in V (Vlang)
fn once<T>(fn fn() T) fn() T {
mut called := false
mut result := T
return fn () T {
if !called {
called = true
result = fn()
}
return result
}
}
fn main() {
initialize := once(fn () int {
println("Initialized")
return 42
})
println(initialize())
println(initialize())
}Debounce with leading edge using timestamp tracking.
- Timestamp: Track last call time
- Delay: Check time difference
- Execution: Execute if enough time passed
- Use case: Search input, API calls
// Debounce in V (Vlang)
import time
fn debounce<T>(delay_ms int, fn fn(T)) fn(T) {
mut last_call := time.now()
return fn (arg T) {
now := time.now()
if now - last_call >= delay_ms * time.millisecond {
last_call = now
fn(arg)
}
}
}
fn main() {
debounced := debounce(1000, fn (msg string) {
println("Executed: ${msg}")
})
debounced("First")
debounced("Second")
debounced("Third")
}Throttle with leading edge using timestamp tracking.
- Timestamp: Track last execution
- Rate limiting: At most once per period
- Execution: Execute if enough time passed
- Use case: Scroll events, resize
// Throttle in V (Vlang)
import time
fn throttle<T>(delay_ms int, fn fn(T)) fn(T) {
mut last_call := time.now()
return fn (arg T) {
now := time.now()
if now - last_call >= delay_ms * time.millisecond {
last_call = now
fn(arg)
}
}
}
fn main() {
throttled := throttle(1000, fn (msg string) {
println("Executed: ${msg}")
})
throttled("First")
throttled("Second")
throttled("Third")
}Deep equality comparison using field-by-field comparison.
- Field comparison: Compare each field
- Nested: Compare nested structs
- Return: Boolean result
- Complexity: O(n) time
// Deep equal in V (Vlang)
struct Address {
city string
zip string
}
struct User {
name string
address Address
}
fn deep_equal(a User, b User) bool {
if a.name != b.name {
return false
}
if a.address.city != b.address.city {
return false
}
if a.address.zip != b.address.zip {
return false
}
return true
}
fn main() {
user1 := User{
name: "Alice",
address: Address{
city: "NYC",
zip: "10001"
}
}
user2 := User{
name: "Alice",
address: Address{
city: "NYC",
zip: "10001"
}
}
println(deep_equal(user1, user2)) // true
}Observable pattern with subscribers and notifications.
- Observable: Maintains subscribers
- Subscribe: Add callback
- Notify: Call all subscribers
- Generic:
<T>support
// Observable pattern in V (Vlang)
struct Observable<T> {
mut:
subscribers []fn(T)
}
fn (mut o Observable<T>) subscribe(callback fn(T)) {
o.subscribers << callback
}
fn (o Observable<T>) notify(data T) {
for subscriber in o.subscribers {
subscriber(data)
}
}
fn main() {
mut observable := Observable[string]{}
observable.subscribe(fn (data string) {
println("Observer 1: ${data}")
})
observable.subscribe(fn (data string) {
println("Observer 2: ${data}")
})
observable.notify("Hello World")
}Singleton pattern using global variable.
- Global variable:
mut instance := Singleton - Getter:
get_singleton() &Singleton - Global access: Through function
- Lazy: Initialize on first use
// Singleton pattern in V (Vlang)
struct Singleton {
mut:
data []string
}
mut singleton_instance := Singleton{}
fn get_singleton() &Singleton {
return &singleton_instance
}
fn main() {
s1 := get_singleton()
s2 := get_singleton()
s1.data << "Hello"
println(s2.data) // ["Hello"]
}Factory pattern using match statement.
- Factory function:
create_user(type, name) - Match: Determine which type to create
- Interface:
interface User - Return: User interface
// Factory pattern in V (Vlang)
interface User {
name string
role() string
}
struct Admin {
name string
}
fn (a Admin) role() string {
return "admin"
}
struct Guest {
name string
}
fn (g Guest) role() string {
return "guest"
}
struct RegularUser {
name string
}
fn (r RegularUser) role() string {
return "regular"
}
fn create_user(type string, name string) User {
match type {
"admin" { return Admin{name} }
"guest" { return Guest{name} }
else { return RegularUser{name} }
}
}
fn main() {
admin := create_user("admin", "Alice")
guest := create_user("guest", "Bob")
println("${admin.name} role: ${admin.role()}")
println("${guest.name} role: ${guest.role()}")
}Strategy pattern using interfaces.
- Strategy interface:
interface PaymentStrategy - Context: Uses strategy
- Runtime switching: Change strategy
- Benefits: Encapsulate algorithms
// Strategy pattern in V (Vlang)
interface PaymentStrategy {
pay(amount f64)
}
struct CreditCardStrategy {}
fn (c CreditCardStrategy) pay(amount f64) {
println("Paid $${amount:.2f} with Credit Card")
}
struct PayPalStrategy {}
fn (p PayPalStrategy) pay(amount f64) {
println("Paid $${amount:.2f} with PayPal")
}
struct CryptoStrategy {}
fn (c CryptoStrategy) pay(amount f64) {
println("Paid $${amount:.2f} with Crypto")
}
struct PaymentContext {
mut:
strategy PaymentStrategy
}
fn (mut c PaymentContext) set_strategy(strategy PaymentStrategy) {
c.strategy = strategy
}
fn (c PaymentContext) execute_payment(amount f64) {
c.strategy.pay(amount)
}
fn main() {
mut context := PaymentContext{
strategy: CreditCardStrategy{}
}
context.execute_payment(100.0)
context.set_strategy(PayPalStrategy{})
context.execute_payment(50.0)
}Observer pattern with subject and observers.
- Subject: Maintains observers
- Observer interface:
interface Observer - Attach/Detach: Add/remove observers
- Notify: Call update on all observers
// Observer pattern in V (Vlang)
interface Observer {
update(data string)
}
struct Subject {
mut:
observers []Observer
state string
}
fn (mut s Subject) attach(observer Observer) {
s.observers << observer
}
fn (mut s Subject) detach(observer Observer) {
for i in 0..s.observers.len {
if s.observers[i] == observer {
s.observers.delete(i)
break
}
}
}
fn (mut s Subject) set_state(state string) {
s.state = state
s.notify_observers()
}
fn (s Subject) notify_observers() {
for observer in s.observers {
observer.update(s.state)
}
}
struct ConcreteObserver {
name string
}
fn (o ConcreteObserver) update(data string) {
println("${o.name} received: ${data}")
}
fn main() {
mut subject := Subject{}
observer1 := ConcreteObserver{name: "Observer1"}
observer2 := ConcreteObserver{name: "Observer2"}
subject.attach(observer1)
subject.attach(observer2)
subject.set_state("Hello World")
}Decorator pattern using wrapper structs.
- Component: Base interface
- Decorator: Wraps component
- Chaining: Multiple decorators
- Benefits: Add behavior dynamically
// Decorator pattern in V (Vlang)
interface Coffee {
cost() f64
description() string
}
struct SimpleCoffee {}
fn (c SimpleCoffee) cost() f64 {
return 5.0
}
fn (c SimpleCoffee) description() string {
return "Coffee"
}
struct MilkDecorator {
coffee Coffee
}
fn (d MilkDecorator) cost() f64 {
return d.coffee.cost() + 2.0
}
fn (d MilkDecorator) description() string {
return d.coffee.description() + ", Milk"
}
struct SugarDecorator {
coffee Coffee
}
fn (d SugarDecorator) cost() f64 {
return d.coffee.cost() + 1.0
}
fn (d SugarDecorator) description() string {
return d.coffee.description() + ", Sugar"
}
fn main() {
mut coffee := Coffee(SimpleCoffee{})
coffee = MilkDecorator{coffee}
coffee = SugarDecorator{coffee}
println(coffee.description()) // Coffee, Milk, Sugar
println(coffee.cost()) // 8.0
}Command pattern with execute and undo methods.
- Command interface:
interface Command - Receiver: Performs work
- Invoker: Executes commands
- Undo/Redo: Command history
// Command pattern in V (Vlang)
interface Command {
execute()
undo()
}
struct AddCommand {
mut:
receiver []int
value int
}
fn (mut c AddCommand) execute() {
c.receiver << c.value
}
fn (mut c AddCommand) undo() {
for i in 0..c.receiver.len {
if c.receiver[i] == c.value {
c.receiver.delete(i)
break
}
}
}
struct CommandManager {
mut:
history []Command
}
fn (mut m CommandManager) execute(cmd Command) {
cmd.execute()
m.history << cmd
}
fn (mut m CommandManager) undo() {
if m.history.len > 0 {
cmd := m.history.pop()
cmd.undo()
}
}
fn main() {
mut receiver := [1, 2, 3]
mut manager := CommandManager{}
cmd := AddCommand{
receiver: receiver,
value: 4
}
manager.execute(cmd)
println(receiver) // [1, 2, 3, 4]
manager.undo()
println(receiver) // [1, 2, 3]
}Memento pattern for state capture and restoration.
- Originator: Creates/restores mementos
- Memento: Stores state
- Caretaker: Manages mementos
- Undo/Redo: State history
// Memento pattern in V (Vlang)
struct Memento {
state string
}
struct Originator {
mut:
state string
}
fn (o Originator) save_state() Memento {
return Memento{o.state}
}
fn (mut o Originator) restore_state(m Memento) {
o.state = m.state
}
struct Caretaker {
mut:
mementos []Memento
}
fn (mut c Caretaker) add_memento(m Memento) {
c.mementos << m
}
fn (c Caretaker) get_memento(index int) ?Memento {
if index < c.mementos.len {
return c.mementos[index]
}
return none
}
fn main() {
mut originator := Originator{}
mut caretaker := Caretaker{}
originator.state = "State 1"
caretaker.add_memento(originator.save_state())
originator.state = "State 2"
caretaker.add_memento(originator.save_state())
originator.state = "State 3"
memento := caretaker.get_memento(0) or { Memento{""} }
originator.restore_state(memento)
println(originator.state) // State 1
}Mediator pattern for centralized communication.
- Mediator: Encapsulates communication
- Colleague: Communicates through mediator
- Benefits: Loose coupling
- Use case: Chat systems
// Mediator pattern in V (Vlang)
struct Mediator {
mut:
colleagues []Colleague
}
fn (mut m Mediator) register(colleague Colleague) {
m.colleagues << colleague
colleague.mediator = m
}
fn (m Mediator) send(message string, sender Colleague) {
for colleague in m.colleagues {
if colleague != sender {
colleague.receive(message)
}
}
}
struct Colleague {
name string
mediator Mediator
}
fn (c Colleague) send(message string) {
c.mediator.send(message, c)
}
fn (c Colleague) receive(message string) {
println("${c.name} received: ${message}")
}
fn main() {
mut mediator := Mediator{}
alice := Colleague{name: "Alice"}
bob := Colleague{name: "Bob"}
mediator.register(alice)
mediator.register(bob)
alice.send("Hello Bob!")
}Chain of Responsibility using interface.
- Handler interface:
interface Handler - Chain: Link handlers
- Benefits: Decoupling
- Use case: Logging, authentication
// Chain of Responsibility in V (Vlang)
interface Handler {
set_next(handler Handler)
handle(request string) ?string
}
struct AuthHandler {
mut:
next Handler
}
fn (mut a AuthHandler) set_next(handler Handler) {
a.next = handler
}
fn (a AuthHandler) handle(request string) ?string {
if request.contains("token") {
println("Authentication passed")
if a.next != 0 {
return a.next.handle(request)
}
return "Success"
}
println("Authentication failed")
return none
}
struct LoggerHandler {
mut:
next Handler
}
fn (mut l LoggerHandler) set_next(handler Handler) {
l.next = handler
}
fn (l LoggerHandler) handle(request string) ?string {
println("Logging request: ${request}")
if l.next != 0 {
return l.next.handle(request)
}
return "Logged"
}
fn main() {
mut auth := AuthHandler{}
mut logger := LoggerHandler{}
auth.set_next(logger)
result := auth.handle("token:valid") or { "Failed" }
println(result)
}State pattern using interface.
- State interface:
interface State - Context: Maintains state
- Transitions: Change between states
- Benefits: Clean state management
// State pattern in V (Vlang)
interface State {
handle(context Context)
}
struct Context {
mut:
state State
}
fn (mut c Context) set_state(state State) {
c.state = state
}
fn (c Context) request() {
c.state.handle(c)
}
struct ReadyState {}
fn (r ReadyState) handle(mut c Context) {
println("Ready: Waiting for input")
c.set_state(ProcessingState{})
}
struct ProcessingState {}
fn (p ProcessingState) handle(mut c Context) {
println("Processing: Working on task")
c.set_state(CompletedState{})
}
struct CompletedState {}
fn (c CompletedState) handle(mut ctx Context) {
println("Completed: Task finished")
}
fn main() {
mut context := Context{
state: ReadyState{}
}
context.request() // Ready
context.request() // Processing
context.request() // Completed
}Proxy pattern using interface.
- Subject interface:
interface Subject - Proxy: Controls access
- Lazy loading: Create on demand
- Benefits: Access control, logging
// Proxy pattern in V (Vlang)
interface Subject {
request() string
}
struct RealSubject {}
fn (r RealSubject) request() string {
return "RealSubject: Handling request"
}
struct Proxy {
mut:
real_subject RealSubject
}
fn (p Proxy) request() string {
if p.check_access() {
return p.real_subject.request()
}
return "Proxy: Access denied"
}
fn (p Proxy) check_access() bool {
println("Proxy: Checking access")
return true
}
fn main() {
proxy := Proxy{}
println(proxy.request())
}Flyweight pattern for sharing objects using map.
- Flyweight: Shared object
- Factory: Manages flyweights
- Benefits: Memory optimization
- Use case: Character rendering
// Flyweight pattern in V (Vlang)
struct Flyweight {
shared_state string
}
fn (f Flyweight) operation(unique_state string) {
println("Shared: ${f.shared_state}, Unique: ${unique_state}")
}
struct FlyweightFactory {
mut:
flyweights map[string]Flyweight
}
fn (mut f FlyweightFactory) get_flyweight(shared_state string) Flyweight {
if shared_state in f.flyweights {
return f.flyweights[shared_state]
}
flyweight := Flyweight{shared_state}
f.flyweights[shared_state] = flyweight
println("Creating new flyweight for: ${shared_state}")
return flyweight
}
fn main() {
mut factory := FlyweightFactory{}
fw1 := factory.get_flyweight("state1")
fw2 := factory.get_flyweight("state1")
fw3 := factory.get_flyweight("state2")
fw1.operation("unique1")
fw2.operation("unique2")
fw3.operation("unique3")
}Bridge pattern for separating abstraction from implementation.
- Abstraction: High-level interface
- Implementation: Low-level operations
- Benefits: Separation of concerns
- Use case: Cross-platform
// Bridge pattern in V (Vlang)
interface Implementation {
operation_impl() string
}
struct ConcreteImplementationA {}
fn (c ConcreteImplementationA) operation_impl() string {
return "ConcreteImplementationA: Operation"
}
struct ConcreteImplementationB {}
fn (c ConcreteImplementationB) operation_impl() string {
return "ConcreteImplementationB: Operation"
}
struct Abstraction {
impl Implementation
}
fn (a Abstraction) operation() string {
return "Abstraction: Additional logic - " + a.impl.operation_impl()
}
fn main() {
impl_a := ConcreteImplementationA{}
impl_b := ConcreteImplementationB{}
abstraction1 := Abstraction{impl_a}
abstraction2 := Abstraction{impl_b}
println(abstraction1.operation())
println(abstraction2.operation())
}Adapter pattern for converting interfaces.
- Target: Expected interface
- Adaptee: Existing interface
- Adapter: Bridges interfaces
- Benefits: Reusability
// Adapter pattern in V (Vlang)
interface Target {
request() string
}
struct Adaptee {}
fn (a Adaptee) specific_request() string {
return "Adaptee: Specific Request"
}
struct Adapter {
adaptee Adaptee
}
fn (a Adapter) request() string {
return a.adaptee.specific_request()
}
fn main() {
adaptee := Adaptee{}
adapter := Adapter{adaptee}
println(adapter.request())
}Facade pattern for simplifying subsystems.
- Facade: Simplified interface
- Subsystem: Complex components
- Benefits: Simplified interface
- Use case: Library APIs
// Facade pattern in V (Vlang)
struct SubsystemA {}
fn (s SubsystemA) operation_a() string {
return "SubsystemA: Operation"
}
struct SubsystemB {}
fn (s SubsystemB) operation_b() string {
return "SubsystemB: Operation"
}
struct SubsystemC {}
fn (s SubsystemC) operation_c() string {
return "SubsystemC: Operation"
}
struct Facade {
a SubsystemA
b SubsystemB
c SubsystemC
}
fn (f Facade) operation() string {
return f.a.operation_a() + " + " + f.b.operation_b() + " + " + f.c.operation_c()
}
fn main() {
facade := Facade{}
println(facade.operation())
}Composite pattern for tree structures.
- Component: Interface for all
- Leaf: Individual object
- Composite: Container
- Benefits: Uniform interface
// Composite pattern in V (Vlang)
interface Component {
operation() string
}
struct Leaf {
name string
}
fn (l Leaf) operation() string {
return "Leaf ${l.name}: Operation"
}
struct Composite {
name string
mut:
children []Component
}
fn (mut c Composite) add(component Component) {
c.children << component
}
fn (c Composite) operation() string {
mut result := "Composite ${c.name}: ["
for child in c.children {
result += child.operation() + ", "
}
result += "]"
return result
}
fn main() {
leaf1 := Leaf{"A"}
leaf2 := Leaf{"B"}
mut composite := Composite{name: "Root"}
composite.add(leaf1)
composite.add(leaf2)
println(composite.operation())
}Visitor pattern for adding operations without modifying elements.
- Visitor: Defines operations
- Element: Accepts visitors
- Benefits: Adding operations without modifying
- Use case: Compilers, AST
// Visitor pattern in V (Vlang)
interface Visitor {
visit_element_a(element ElementA) string
visit_element_b(element ElementB) string
}
interface Element {
accept(visitor Visitor) string
}
struct ElementA {
data string
}
fn (e ElementA) accept(visitor Visitor) string {
return visitor.visit_element_a(e)
}
struct ElementB {
data string
}
fn (e ElementB) accept(visitor Visitor) string {
return visitor.visit_element_b(e)
}
struct ConcreteVisitor {}
fn (c ConcreteVisitor) visit_element_a(e ElementA) string {
return "Visiting ElementA with data: ${e.data}"
}
fn (c ConcreteVisitor) visit_element_b(e ElementB) string {
return "Visiting ElementB with data: ${e.data}"
}
fn main() {
visitor := ConcreteVisitor{}
element_a := ElementA{"A data"}
element_b := ElementB{"B data"}
println(element_a.accept(visitor))
println(element_b.accept(visitor))
}Iterator pattern for sequential access.
- Iterator: Traverses collection
- has_next: Check for more items
- next: Return next item
- Generic:
<T>support
// Iterator pattern in V (Vlang)
struct Iterator<T> {
collection []T
mut:
index int
}
fn (mut i Iterator<T>) next() ?T {
if i.index < i.collection.len {
value := i.collection[i.index]
i.index++
return value
}
return none
}
fn (i Iterator<T>) has_next() bool {
return i.index < i.collection.len
}
struct Collection<T> {
items []T
}
fn (c Collection<T>) iterator() Iterator<T> {
return Iterator<T>{c.items}
}
fn main() {
collection := Collection<int>{[1, 2, 3, 4, 5]}
mut iter := collection.iterator()
for iter.has_next() {
value := iter.next() or { break }
println(value)
}
}Template Method for algorithm skeletons.
- AbstractClass: Defines template
- ConcreteClass: Implements steps
- Benefits: Code reuse
- Use case: Frameworks
// Template Method pattern in V (Vlang)
interface AbstractClass {
template_method() string
step1() string
step2() string
step3() string
}
fn (a AbstractClass) template_method() string {
return a.step1() + " -> " + a.step2() + " -> " + a.step3()
}
struct ConcreteClass {}
fn (c ConcreteClass) step1() string {
return "Step 1"
}
fn (c ConcreteClass) step2() string {
return "Concrete Step 2"
}
fn (c ConcreteClass) step3() string {
return "Step 3"
}
fn main() {
concrete := ConcreteClass{}
println(concrete.template_method())
}Builder pattern for constructing complex objects.
- Builder: Constructs parts
- Director: Orchestrates construction
- Product: Constructed object
- Benefits: Step-by-step construction
// Builder pattern in V (Vlang)
struct Product {
mut:
parts []string
}
fn (mut p Product) add(part string) {
p.parts << part
}
fn (p Product) list_parts() string {
return p.parts.join(", ")
}
struct Builder {
mut:
product Product
}
fn (mut b Builder) reset() {
b.product = Product{}
}
fn (mut b Builder) build_step_a() {
b.product.add("Part A")
}
fn (mut b Builder) build_step_b() {
b.product.add("Part B")
}
fn (b Builder) get_result() Product {
return b.product
}
struct Director {
builder Builder
}
fn (d Director) build_minimal() {
d.builder.build_step_a()
}
fn (d Director) build_full() {
d.builder.build_step_a()
d.builder.build_step_b()
}
fn main() {
mut builder := Builder{}
director := Director{builder}
director.build_minimal()
product := builder.get_result()
println(product.list_parts()) // Part A
}Prototype pattern for cloning objects.
- Prototype: Cloneable object
- Clone: Creates a copy
- Deep clone: Recursive copy
- Benefits: Object reuse, performance
// Prototype pattern in V (Vlang)
struct Prototype {
name string
nested map[string]int
}
fn (p Prototype) clone() Prototype {
return Prototype{
name: p.name,
nested: p.nested
}
}
fn (p Prototype) deep_clone() Prototype {
mut new_nested := map[string]int{}
for key, value in p.nested {
new_nested[key] = value
}
return Prototype{
name: p.name,
nested: new_nested
}
}
fn main() {
original := Prototype{
name: "Original",
nested: {"value": 42}
}
copy := original.clone()
copy.nested["value"] = 99
println(original.nested) // {"value": 42}
deep_copy := original.deep_clone()
deep_copy.nested["value"] = 100
println(original.nested) // {"value": 42}
}Error handling using option types and custom errors.
- Option types:
?int - or block:
or { err } - Custom errors:
struct ValidationError - Error propagation: Return
?
// Error Handling in V (Vlang)
// Custom error type
struct ValidationError {
field string
message string
}
fn (e ValidationError) str() string {
return "Validation error in ${e.field}: ${e.message}"
}
// Function returning option
fn validate_age(age int) ? {
if age < 0 {
return error("Age cannot be negative")
}
if age > 150 {
return error("Invalid age")
}
}
// Using with or block
fn main() {
err := validate_age(25) or {
println("Error: ${err}")
return
}
println("Age is valid")
// Using if
if err := validate_age(-5) {
println("Valid")
} else {
println("Invalid")
}
// Custom error type
fn validate_email(email string) ? {
if !email.contains("@") {
return ValidationError{"email", "Invalid email format"}
}
}
err2 := validate_email("invalid") or {
println("Error: ${err}")
return
}
}File I/O operations using os module.
- Write:
os.write_file() - Read:
os.read_file() - Append:
os.append_file() - Delete:
os.rm()
// File I/O in V (Vlang)
import os
fn main() {
// Write to file
content := "Hello, World!"
os.write_file("hello.txt", content) or {
println("Error writing file: ${err}")
return
}
println("File written successfully")
// Read from file
data := os.read_file("hello.txt") or {
println("Error reading file: ${err}")
return
}
println("File content: ${data}")
// Append to file
os.append_file("hello.txt", "
Appended line") or {
println("Error appending: ${err}")
return
}
// Check if file exists
if os.exists("hello.txt") {
println("File exists")
}
// Delete file
os.rm("hello.txt") or {
println("Error deleting file: ${err}")
return
}
}JSON serialization and deserialization using json module.
- Encode:
json.encode() - Decode:
json.decode() - Struct tags: Field names match JSON
- Error handling: Use
orblock
// JSON Handling in V (Vlang)
import json
struct User {
name string
age int
email string
}
fn main() {
// Serialize to JSON
user := User{
name: "Alice",
age: 25,
email: "alice@example.com"
}
json_data := json.encode(user)
println(json_data)
// Deserialize from JSON
json_str := '{"name":"Bob","age":30,"email":"bob@example.com"}'
parsed := json.decode(User, json_str) or {
println("Error parsing JSON: ${err}")
return
}
println("Name: ${parsed.name}, Age: ${parsed.age}")
}Database operations using db.sqlite module.
- Connect:
sqlite.connect() - Create table:
db.exec() - Insert:
db.exec()with parameters - Query:
db.query()
// Database Operations in V (Vlang)
import db.sqlite
struct User {
id int
name string
age int
email string
}
fn main() {
// Open database
db := sqlite.connect("test.db") or {
println("Error connecting to database: ${err}")
return
}
defer {
db.close() or { println("Error closing database: ${err}") }
}
// Create table
db.exec("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER, email TEXT)") or {
println("Error creating table: ${err}")
return
}
// Insert data
db.exec("INSERT INTO users (name, age, email) VALUES (?, ?, ?)", "Alice", 25, "alice@example.com") or {
println("Error inserting data: ${err}")
return
}
// Query data
rows := db.query("SELECT id, name, age, email FROM users") or {
println("Error querying data: ${err}")
return
}
for row in rows {
println("ID: ${row.id}, Name: ${row.name}, Age: ${row.age}, Email: ${row.email}")
}
}Testing using V's built-in test framework.
- Test files:
_test.v - Test functions:
fn test_name() - Assertions:
assert condition - Run tests:
v test .
// Testing in V (Vlang)
// test file: main_test.v
/*
import testing
fn test_add() {
assert add(2, 3) == 5
assert add(-1, 1) == 0
}
fn test_divide() {
assert divide(10, 2) == 5
assert divide(10, 0) == 0 // Should handle division by zero
}
*/
// Example functions to test
fn add(a int, b int) int {
return a + b
}
fn divide(a int, b int) int {
if b == 0 {
return 0
}
return a / b
}
fn main() {
println("Running tests...")
// In V, tests are run with: v test .
}Web server using net.http.server module.
- Server:
server.Server{port: 8080} - Handle:
server.handle("/", fn) - Request:
http.Request - Response:
res.write()
// Web Server in V (Vlang)
import net.http
import net.http.server
// Simple HTTP server
fn main() {
mut server := server.Server{
port: 8080
}
server.handle("/", fn (req http.Request, res mut http.Response) {
res.write("Hello, World!")
})
server.handle("/api/users", fn (req http.Request, res mut http.Response) {
users := '[{"name":"Alice","age":25},{"name":"Bob","age":30}]'
res.headers.set("Content-Type", "application/json")
res.write(users)
})
println("Server running on http://localhost:8080")
server.serve() or {
println("Error starting server: ${err}")
}
}CLI application using flag module.
- Flag parser:
flag.new_flag_parser() - String flag:
fp.string() - Int flag:
fp.int() - Bool flag:
fp.bool()
// CLI Application in V (Vlang)
import os
import flag
fn main() {
mut fp := flag.new_flag_parser(os.args)
name := fp.string("name", 'n', "", "Name to greet")
age := fp.int("age", 'a', 0, "Age of the person")
verbose := fp.bool("verbose", 'v', false, "Verbose output")
if name == "" {
println("Usage: app -name <name> -age <age> -v")
return
}
if verbose {
println("Name: ${name}")
println("Age: ${age}")
}
println("Hello, ${name}! You are ${age} years old.")
}Creating and using modules with pub keyword.
- Module:
module name - Export:
pub fn - Import:
import module - Usage:
module.function()
// Modules and Packages in V (Vlang)
// math.v (module)
module math
pub fn add(a int, b int) int {
return a + b
}
pub fn subtract(a int, b int) int {
return a - b
}
// main.v
import math
fn main() {
println(math.add(5, 3)) // 8
println(math.subtract(10, 4)) // 6
}Generics with type constraints in V.
- Generic struct:
struct Box[T] - Generic function:
fn sum<T>(a T, b T) T - Multiple types:
fn swap[T, U] - Type inference: Automatically inferred
// Generics and Type Constraints in V (Vlang)
// Generic struct
struct Box[T] {
value T
}
fn (b Box[T]) get() T {
return b.value
}
// Generic function with constraints
fn sum_numbers<T>(a T, b T) T {
return a + b
}
// Generic with multiple types
fn swap<T, U>(a T, b U) (U, T) {
return b, a
}
// Main
fn main() {
box_int := Box[int]{42}
box_string := Box[string]{"Hello"}
println(box_int.get()) // 42
println(box_string.get()) // Hello
println(sum_numbers(5, 3)) // 8
println(sum_numbers(10.5, 3.5)) // 14.0
a, b := swap(1, "Hello")
println("${a}, ${b}") // Hello, 1
}Best practices for writing clean, efficient V code.
- Immutability: Use immutable variables by default
- Option types: Use for nullable values
- Match: Use exhaustive pattern matching
- Structs: Use for data grouping
- Interfaces: Use for polymorphism
// V Best Practices
// 1. Use immutability by default
fn main() {
// Immutable variables (default)
name := "Alice"
// name = "Bob" // Error: cannot assign to immutable variable
// Mutable variables
mut age := 25
age++
// 2. Use option types for nullable values
maybe_string := ?string("Hello")
value := maybe_string or { "default" }
println(value)
// 3. Use match for exhaustive pattern matching
status := "active"
match status {
"active" { println("Active") }
"inactive" { println("Inactive") }
else { println("Unknown") }
}
// 4. Use structs for data grouping
person := Person{
name: "Alice",
age: 25
}
// 5. Use interfaces for polymorphism
// 6. Use channels for concurrency
// 7. Use defer for cleanup
defer {
println("Cleanup")
}
// 8. Use const for constants
const PI = 3.14159
// 9. Use enums for fixed values
// 10. Use sum types for sealed classes
}