There is an exception to the rule that all operators in V must have values of the same type on both sides. A small primitive type on one side can be automatically promoted if it fits completely into the data range of the type on the other side. These are the allowed possibilities:

    1. i8 i16 int i64
    2. f32 f64
    3. byte u16 u32 u64
    4. ptr
    5. i8 i16 int i64

    An int value for example can be automatically promoted to f64 or i64 but not to f32 or u32. (f32 would mean precision loss for large values and u32 would mean loss of the sign for negative values).

    Strings

    1. name := 'Bob'
    2. println(name.len)
    3. println(name[0]) // indexing gives a byte B
    4. println(name[1..3]) // slicing gives a string 'ob'
    5. windows_newline := '\r\n' // escape special characters like in C
    6. assert windows_newline.len == 2

    In V, a string is a read-only array of bytes. String data is encoded using UTF-8. String values are immutable. You cannot mutate elements:

    1. mut s := 'hello 🌎'
    2. s[0] = `H` // not allowed

    Note that indexing a string will produce a byte, not a rune. Indexes correspond to bytes in the string, not Unicode code points.

    Character literals have type rune. To denote them, use `

    1. rocket := `🚀`
    2. assert 'aloha!'[0] == `a`

    Both single and double quotes can be used to denote strings. For consistency, vfmt converts double quotes to single quotes unless the string contains a single quote character.

    For raw strings, prepend r. Raw strings are not escaped:

    1. s := r'hello\nworld'
    2. println(s) // "hello\nworld"

    Basic interpolation syntax is pretty simple - use $ before a variable name. The variable will be converted to a string and embedded into the literal:

    1. name := 'Bob'
    2. println('Hello, $name!') // Hello, Bob!

    It also works with fields: 'age = $user.age'. If you need more complex expressions, use ${}: 'can register = ${user.age > 13}'.

    Format specifiers similar to those in C’s printf() are also supported. f, g, x, etc. are optional and specify the output format. The compiler takes care of the storage size, so there is no hd or llu.

    1. x := 123.4567
    2. println('x = ${x:4.2f}')
    3. println('[${x:10}]') // pad with spaces on the left
    4. println('[${int(x):-10}]') // pad with spaces on the right

    String operators

    1. name := 'Bob'
    2. println(bobby) // "Bobby"
    3. s += 'world' // `+=` is used to append to a string
    4. println(s) // "hello world"

    All operators in V must have values of the same type on both sides. You cannot concatenate an integer to a string:

    We have to either convert age to a string:

    1. age := 11
    2. println('age = ' + age.str())

    or use string interpolation (preferred):

    1. age := 12
    2. println('age = $age')
    1. a := 123

    This will assign the value of 123 to a. By default a will have the type int.

    You can also use hexadecimal, binary or octal notation for integer literals:

    1. a := 0x7B
    2. b := 0b01111011
    3. c := 0o173

    All of these will be assigned the same value, 123. They will all have type int, no matter what notation you used.

    V also supports writing numbers with _ as separator:

    1. num := 1_000_000 // same as 1000000
    2. three := 0b0_11 // same as 0b11
    3. float_num := 3_122.55 // same as 3122.55
    4. hexa := 0xF_F // same as 255
    5. oct := 0o17_3 // same as 0o173

    If you want a different type of integer, you can use casting:

    1. a := i64(123)
    2. b := byte(42)
    3. c := i16(12345)

    Assigning floating point numbers works the same way:

    1. f := 1.0
    2. f1 := f64(3.14)
    3. f2 := f32(3.14)

    If you do not specify the type explicitly, by default float literals will have the type of f64.

    Arrays

    1. mut nums := [1, 2, 3]
    2. println(nums) // "[1, 2, 3]"
    3. println(nums[1]) // "2"
    4. nums[1] = 5
    5. println(nums) // "[1, 5, 3]"
    6. println(nums[0..2]) // slicing gives an array "[1, 5]"
    7. println(nums.len) // "3"
    8. nums = [] // The array is now empty
    9. println(nums.len) // "0"
    10. // Declare an empty array:
    11. users := []int{}

    The type of an array is determined by the first element:

    • [1, 2, 3] is an array of ints ([]int).

    The user can explicitly specify the type for the first element: [byte(16), 32, 64, 128]. V arrays are homogeneous (all elements must have the same type). This means that code like [1, 'a'] will not compile.

    The .len field returns the length of the array. Note that it’s a read-only field, and it can’t be modified by the user. Exported fields are read-only by default in V. See .

    Array operations

    returns true if the array contains val. See in operator.

    Initializing array properties

    During initialization you can specify the capacity of the array (cap), its initial length (len), and the default element (init):

    1. arr := []int{len: 5, init: -1}
    2. // `[-1, -1, -1, -1, -1]`

    Setting the capacity improves performance of insertions, as it reduces the number of reallocations needed:

    1. mut numbers := []int{ cap: 1000 }
    2. println(numbers.len) // 0
    3. // Now appending elements won't reallocate
    4. for i in 0 .. 1000 {
    5. numbers << i
    6. }

    Note: The above code uses a statement.

    Array methods

    All arrays can be easily printed with println(arr) and converted to a string with s := arr.str().

    Copying the data from the array is done with .clone():

    1. nums := [1, 2, 3]
    2. nums_copy := nums.clone()

    Arrays can be efficiently filtered and mapped with the .filter() and .map() methods:

    1. nums := [1, 2, 3, 4, 5, 6]
    2. even := nums.filter(it % 2 == 0)
    3. println(even) // [2, 4, 6]
    4. // filter can accept anonymous functions
    5. even_fn := nums.filter(fn (x int) bool {
    6. return x % 2 == 0
    7. })
    8. println(even_fn)
    9. words := ['hello', 'world']
    10. upper := words.map(it.to_upper())
    11. println(upper) // ['HELLO', 'WORLD']
    12. // map can also accept anonymous functions
    13. upper_fn := words.map(fn (w string) string {
    14. return w.to_upper()
    15. })
    16. println(upper_fn) // ['HELLO', 'WORLD']

    it is a builtin variable which refers to element currently being processed in filter/map methods.

    Multidimensional Arrays

    Arrays can have more than one dimension.

    2d array example:

    1. mut a := [][]int{len:2, init: []int{len:3}}
    2. a[0][1] = 2
    3. println(a) // [[0, 2, 0], [0, 0, 0]]

    3d array example:

    1. mut a := [][][]int{len:2, init: [][]int{len:3, init: []int{len:2}}}
    2. a[0][1][1] = 2
    3. println(a) // [[[0, 0], [0, 2], [0, 0]], [[0, 0], [0, 0], [0, 0]]]

    Sorting arrays

    Sorting arrays of all kinds is very simple and intuitive. Special variables a and b are used when providing a custom sorting condition.

    1. mut numbers := [1, 3, 2]
    2. numbers.sort() // 1, 2, 3
    3. numbers.sort(a > b) // 3, 2, 1
    1. struct User { age int name string }
    2. mut users := [User{21, 'Bob'}, User{20, 'Zarkon'}, User{25, 'Alice'}]
    3. users.sort(a.age < b.age) // sort by User.age int field