Beginner

Operators in V (Vlang)

Learn arithmetic, comparison, logical, assignment, and bitwise operators in V with practical Docker-ready examples

Operators are the verbs of a programming language – they take values and produce new ones. V keeps its operator set deliberately small and predictable, in line with its design goal of “no hidden control flow.” There is no operator overloading magic to memorize for built-in types, no surprising precedence, and no exotic symbols.

V is statically and strongly typed, which shapes how operators behave. Unlike dynamic languages, V will not silently mix an integer and a float in arithmetic – you must use operands of the same type. This catches a whole category of bugs at compile time. V’s immutability-by-default rule also means the compound assignment operators (+=, -=, and friends) only work on variables you have explicitly declared mut.

In this tutorial you will learn V’s arithmetic, comparison, logical, assignment, and bitwise operators, how operator precedence decides evaluation order, and how strings are concatenated. Every example is a complete program you can run with Docker.

Arithmetic Operators

V provides the five standard arithmetic operators. The important subtlety is that / performs integer division when both operands are integers (the fractional part is truncated), and floating-point division only when the operands are floats.

Create a file named arithmetic.v:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
fn main() {
	a := 17
	b := 5

	println('a + b  = ${a + b}') // addition
	println('a - b  = ${a - b}') // subtraction
	println('a * b  = ${a * b}') // multiplication
	println('a / b  = ${a / b}') // integer division (truncates)
	println('a % b  = ${a % b}') // modulo (remainder)

	// Float division requires float operands
	p := 9.0
	q := 4.0
	println('p / q  = ${p / q}') // floating-point division
}

Note that a / b gives 3, not 3.4 – because both a and b are integers, V truncates toward zero. To get a fractional result you need float operands like p and q. V has no ** power operator; use math.pow from the standard library for exponentiation.

Comparison and Logical Operators

Comparison operators (==, !=, <, >, <=, >=) return a bool. The logical operators && (and), || (or), and ! (not) combine boolean values, and they short-circuit – the right-hand side is only evaluated if needed.

Create a file named comparison.v:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
	x := 10
	y := 20

	println('x == y : ${x == y}')
	println('x != y : ${x != y}')
	println('x <  y : ${x < y}')
	println('x >  y : ${x > y}')
	println('x <= y : ${x <= y}')
	println('x >= y : ${x >= y}')

	// Logical operators combine boolean expressions
	age := 25
	is_adult := age >= 18 && age < 65
	println('is_adult  : ${is_adult}')

	has_ticket := false
	can_enter := is_adult && has_ticket
	println('can_enter : ${can_enter}')

	// ! negates a boolean
	is_free := !has_ticket
	println('is_free   : ${is_free}')
}

Assignment Operators

The := operator declares and initializes a variable, inferring its type. Plain = reassigns an existing variable. The compound assignment operators (+=, -=, *=, /=, %=) apply an operation and store the result back – but because V variables are immutable by default, the variable must be declared with mut.

Create a file named assignment.v:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
	mut total := 100

	total += 10 // total = total + 10
	println('after += : ${total}')

	total -= 20
	println('after -= : ${total}')

	total *= 2
	println('after *= : ${total}')

	total /= 3 // integer division
	println('after /= : ${total}')

	total %= 7
	println('after %= : ${total}')

	// The + operator concatenates strings; += appends
	mut message := 'V'
	message += 'lang'
	message += ' rocks'
	println('message  : ${message}')
}

If you forget the mut keyword on total, V refuses to compile and tells you the variable is declared as immutable – a deliberate safety feature.

Bitwise Operators

For low-level work, V offers the full set of bitwise operators on integers: & (and), | (or), ^ (xor), << (left shift), and >> (right shift). V also supports binary literals with the 0b prefix, which makes the bit patterns easy to follow.

Create a file named bitwise.v:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
fn main() {
	a := 0b1100 // 12
	b := 0b1010 // 10

	println('a & b  = ${a & b}')  // bitwise AND
	println('a | b  = ${a | b}')  // bitwise OR
	println('a ^ b  = ${a ^ b}')  // bitwise XOR
	println('a << 1 = ${a << 1}') // left shift (x2)
	println('a >> 1 = ${a >> 1}') // right shift (/2)
}

A left shift by one is equivalent to multiplying by 2 (12 << 1 = 24), and a right shift by one is integer division by 2 (12 >> 1 = 6).

Operator Precedence

When an expression mixes operators, precedence decides what happens first. V follows the familiar rules: *, /, and % bind tighter than + and -; comparison operators bind tighter than &&; and && binds tighter than ||. Use parentheses when you want to be explicit.

Create a file named precedence.v:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
fn main() {
	// Multiplication happens before addition
	result1 := 2 + 3 * 4
	println('2 + 3 * 4    = ${result1}')

	// Parentheses force the addition first
	result2 := (2 + 3) * 4
	println('(2 + 3) * 4  = ${result2}')

	// && binds tighter than ||, so b && c is evaluated first
	a := true
	b := false
	c := true
	result3 := a || b && c
	println('a || b && c  = ${result3}')

	// Comparisons evaluate before the logical &&
	n := 7
	in_range := n > 0 && n < 10
	println('0 < n < 10   = ${in_range}')
}

Running with Docker

You can run any of these examples with the official V image – no local install required. Swap the filename to run a different example.

1
2
3
4
5
6
7
8
# Pull the official V image
docker pull thevlang/vlang:alpine

# Run the arithmetic example
docker run --rm -v $(pwd):/app -w /app thevlang/vlang:alpine v run arithmetic.v

# Run any other example by changing the filename
docker run --rm -v $(pwd):/app -w /app thevlang/vlang:alpine v run precedence.v

Expected Output

Running arithmetic.v:

a + b  = 22
a - b  = 12
a * b  = 85
a / b  = 3
a % b  = 2
p / q  = 2.25

Running comparison.v:

x == y : false
x != y : true
x <  y : true
x >  y : false
x <= y : true
x >= y : false
is_adult  : true
can_enter : false
is_free   : true

Running assignment.v:

after += : 110
after -= : 90
after *= : 180
after /= : 60
after %= : 4
message  : Vlang rocks

Running bitwise.v:

a & b  = 8
a | b  = 14
a ^ b  = 6
a << 1 = 24
a >> 1 = 6

Running precedence.v:

2 + 3 * 4    = 14
(2 + 3) * 4  = 20
a || b && c  = true
0 < n < 10   = true

Key Concepts

  • Integer vs. float division/ truncates when both operands are integers; you need float operands (like 9.0) to get a fractional result.
  • Strong typing, no implicit mixing – V will not silently combine an int and an f64 in an arithmetic expression; convert one explicitly first.
  • No power operator – V has no **; use math.pow from the standard library for exponentiation.
  • Compound assignment needs mut+=, -=, and friends only work on variables declared mutable, because V values are immutable by default.
  • Short-circuit logic&& and || stop evaluating as soon as the result is determined.
  • + concatenates strings – the same operator that adds numbers joins strings, and += appends to a mutable string.
  • Predictable precedence*///% before +/-, comparisons before &&, and && before ||; reach for parentheses when in doubt.
  • Binary literals – the 0b prefix lets you write bit patterns directly, which pairs naturally with the bitwise operators.

Running Today

All examples can be run using Docker:

docker pull thevlang/vlang:alpine
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining