2019-11-21 04:36:55 +03:00
|
|
|
|
# V Documentation
|
|
|
|
|
|
|
|
|
|
## Introduction
|
|
|
|
|
|
|
|
|
|
V is a statically typed compiled programming language designed for building maintainable software.
|
|
|
|
|
|
2020-08-16 20:16:59 +03:00
|
|
|
|
It's similar to Go and its design has also been influenced by Oberon, Rust, Swift,
|
|
|
|
|
Kotlin, and Python.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-17 14:28:29 +03:00
|
|
|
|
V is a very simple language. Going through this documentation will take you about an hour,
|
2020-05-07 13:30:41 +03:00
|
|
|
|
and by the end of it you will have pretty much learned the entire language.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
The language promotes writing simple and clear code with minimal abstraction.
|
2020-05-05 16:42:53 +03:00
|
|
|
|
|
2020-10-01 23:41:17 +03:00
|
|
|
|
Despite being simple, V gives the developer a lot of power.
|
|
|
|
|
Anything you can do in other languages, you can do in V.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-05-10 16:49:43 +03:00
|
|
|
|
## Table of Contents
|
2020-05-11 21:21:41 +03:00
|
|
|
|
|
2020-05-10 16:49:43 +03:00
|
|
|
|
<table>
|
2020-06-26 21:01:30 +03:00
|
|
|
|
<tr><td width=33% valign=top>
|
2020-06-26 21:00:52 +03:00
|
|
|
|
|
|
|
|
|
* [Hello world](#hello-world)
|
|
|
|
|
* [Comments](#comments)
|
|
|
|
|
* [Functions](#functions)
|
2020-07-23 19:55:54 +03:00
|
|
|
|
* [Returning multiple values](#returning-multiple-values)
|
2020-08-09 05:12:08 +03:00
|
|
|
|
* [Variable number of arguments](#variable-number-of-arguments)
|
2020-07-23 19:55:54 +03:00
|
|
|
|
* [Symbol visibility](#symbol-visibility)
|
2020-06-26 21:00:52 +03:00
|
|
|
|
* [Variables](#variables)
|
|
|
|
|
* [Types](#types)
|
|
|
|
|
* [Strings](#strings)
|
2020-07-14 20:26:14 +03:00
|
|
|
|
* [Numbers](#numbers)
|
2020-06-26 21:00:52 +03:00
|
|
|
|
* [Arrays](#arrays)
|
|
|
|
|
* [Maps](#maps)
|
2020-07-23 19:55:54 +03:00
|
|
|
|
* [Module imports](#module-imports)
|
|
|
|
|
* [Statements & expressions](#statements--expressions)
|
2020-06-26 21:00:52 +03:00
|
|
|
|
* [If](#if)
|
2020-07-23 19:55:54 +03:00
|
|
|
|
* [In operator](#in-operator)
|
2020-06-26 21:00:52 +03:00
|
|
|
|
* [For loop](#for-loop)
|
|
|
|
|
* [Match](#match)
|
|
|
|
|
* [Defer](#defer)
|
|
|
|
|
* [Structs](#structs)
|
2020-07-23 19:55:54 +03:00
|
|
|
|
* [Embedded structs](#embedded-structs)
|
|
|
|
|
* [Default field values](#default-field-values)
|
2020-07-23 01:37:37 +03:00
|
|
|
|
* [Short struct literal syntax](#short-struct-initialization-syntax)
|
2020-06-26 21:00:52 +03:00
|
|
|
|
* [Access modifiers](#access-modifiers)
|
|
|
|
|
* [Methods](#methods)
|
|
|
|
|
|
2020-06-26 21:01:30 +03:00
|
|
|
|
</td><td width=33% valign=top>
|
2020-06-26 21:00:52 +03:00
|
|
|
|
|
|
|
|
|
* [Functions 2](#functions-2)
|
|
|
|
|
* [Pure functions by default](#pure-functions-by-default)
|
2020-06-30 15:29:55 +03:00
|
|
|
|
* [Mutable arguments](#mutable-arguments)
|
2020-06-26 21:00:52 +03:00
|
|
|
|
* [Anonymous & high order functions](#anonymous--high-order-functions)
|
|
|
|
|
* [References](#references)
|
|
|
|
|
* [Modules](#modules)
|
|
|
|
|
* [Constants](#constants)
|
2020-12-28 21:29:58 +03:00
|
|
|
|
* [Builtin functions](#builtin-functions)
|
|
|
|
|
* [Printing custom types](#printing-custom-types)
|
2020-06-26 21:00:52 +03:00
|
|
|
|
* [Types 2](#types-2)
|
|
|
|
|
* [Interfaces](#interfaces)
|
|
|
|
|
* [Enums](#enums)
|
|
|
|
|
* [Sum types](#sum-types)
|
|
|
|
|
* [Option/Result types & error handling](#optionresult-types-and-error-handling)
|
|
|
|
|
* [Generics](#generics)
|
|
|
|
|
* [Concurrency](#concurrency)
|
2020-09-20 17:20:56 +03:00
|
|
|
|
* [Channels](#channels)
|
2020-06-26 21:00:52 +03:00
|
|
|
|
* [Decoding JSON](#decoding-json)
|
|
|
|
|
* [Testing](#testing)
|
|
|
|
|
* [Memory management](#memory-management)
|
|
|
|
|
* [ORM](#orm)
|
2020-07-14 20:26:14 +03:00
|
|
|
|
|
|
|
|
|
</td><td valign=top>
|
|
|
|
|
|
2020-06-26 21:00:52 +03:00
|
|
|
|
* [Writing documentation](#writing-documentation)
|
|
|
|
|
* [Tools](#tools)
|
2020-12-27 14:36:00 +03:00
|
|
|
|
* [v fmt](#v-fmt)
|
2020-06-26 21:00:52 +03:00
|
|
|
|
* [Profiling](#profiling)
|
2020-12-27 14:36:00 +03:00
|
|
|
|
* [Advanced Topics](#advanced-topics)
|
2020-07-23 01:36:23 +03:00
|
|
|
|
* [Memory-unsafe code](#memory-unsafe-code)
|
2020-06-26 21:00:52 +03:00
|
|
|
|
* [Calling C functions from V](#calling-c-functions-from-v)
|
2020-07-14 20:24:59 +03:00
|
|
|
|
* [Debugging generated C code](#debugging-generated-c-code)
|
2020-06-26 21:00:52 +03:00
|
|
|
|
* [Conditional compilation](#conditional-compilation)
|
|
|
|
|
* [Compile time pseudo variables](#compile-time-pseudo-variables)
|
2020-08-11 18:57:39 +03:00
|
|
|
|
* [Compile-time reflection](#compile-time-reflection)
|
2020-06-26 21:00:52 +03:00
|
|
|
|
* [Limited operator overloading](#limited-operator-overloading)
|
|
|
|
|
* [Inline assembly](#inline-assembly)
|
2020-12-17 14:25:31 +03:00
|
|
|
|
* [Translating C to V](#translating-c-to-v)
|
2020-06-26 21:00:52 +03:00
|
|
|
|
* [Hot code reloading](#hot-code-reloading)
|
|
|
|
|
* [Cross compilation](#cross-compilation)
|
|
|
|
|
* [Cross-platform shell scripts in V](#cross-platform-shell-scripts-in-v)
|
|
|
|
|
* [Attributes](#attributes)
|
|
|
|
|
* [Appendices](#appendices)
|
|
|
|
|
* [Keywords](#appendix-i-keywords)
|
|
|
|
|
* [Operators](#appendix-ii-operators)
|
2020-06-26 20:36:40 +03:00
|
|
|
|
|
2020-06-26 21:12:52 +03:00
|
|
|
|
</td></tr>
|
2020-05-10 16:49:43 +03:00
|
|
|
|
</table>
|
2020-05-11 21:21:41 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
<!--
|
2020-12-01 18:09:33 +03:00
|
|
|
|
There are several special keywords, which you can put after the code fences for v.
|
2020-11-03 03:04:14 +03:00
|
|
|
|
These are:
|
|
|
|
|
compile - default, you do not need to specify it. cmd/tools/check-md.v compile the example.
|
|
|
|
|
ignore - ignore the example, useful for examples that just use the syntax highlighting
|
|
|
|
|
failcompile - known failing compilation. Useful for examples demonstrating compiler errors.
|
|
|
|
|
oksyntax - it should parse, it may not compile. Useful for partial examples.
|
|
|
|
|
badsyntax - known bad syntax, it should not even parse
|
|
|
|
|
wip - like ignore; a planned feature; easy to search.
|
|
|
|
|
-->
|
2020-05-05 16:42:53 +03:00
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
## Hello World
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```v
|
|
|
|
|
fn main() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println('hello world')
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
2020-10-01 23:41:17 +03:00
|
|
|
|
|
|
|
|
|
Save this snippet into a file named `hello.v`. Now do: `v run hello.v`.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-07-09 18:14:14 +03:00
|
|
|
|
> That is assuming you have symlinked your V with `v symlink`, as described
|
2020-05-31 12:12:37 +03:00
|
|
|
|
[here](https://github.com/vlang/v/blob/master/README.md#symlinking).
|
2020-10-01 23:41:17 +03:00
|
|
|
|
If you haven't yet, you have to type the path to V manually.
|
2020-05-31 08:42:34 +03:00
|
|
|
|
|
2020-10-01 23:41:17 +03:00
|
|
|
|
Congratulations - you just wrote and executed your first V program!
|
2020-05-31 08:42:34 +03:00
|
|
|
|
|
2020-10-01 23:41:17 +03:00
|
|
|
|
You can compile a program without execution with `v hello.v`.
|
2020-05-31 12:12:37 +03:00
|
|
|
|
See `v help` for all supported commands.
|
2020-05-31 08:42:34 +03:00
|
|
|
|
|
2020-10-01 23:41:17 +03:00
|
|
|
|
From the example above, you can see that functions are declared with the `fn` keyword.
|
|
|
|
|
The return type is specified after the function name.
|
2020-12-22 18:19:55 +03:00
|
|
|
|
In this case `main` doesn't return anything, so there is no return type.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-22 18:19:55 +03:00
|
|
|
|
As in many other languages (such as C, Go, and Rust), `main` is the entry point of your program.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-01 23:41:17 +03:00
|
|
|
|
`println` is one of the few built-in functions.
|
|
|
|
|
It prints the value passed to it to standard output.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
`fn main()` declaration can be skipped in one file programs.
|
2020-10-01 23:41:17 +03:00
|
|
|
|
This is useful when writing small programs, "scripts", or just learning the language.
|
|
|
|
|
For brevity, `fn main()` will be skipped in this tutorial.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-01 23:41:17 +03:00
|
|
|
|
This means that a "hello world" program in V is as simple as
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
println('hello world')
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Comments
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
// This is a single line comment.
|
2020-12-06 00:54:41 +03:00
|
|
|
|
/*
|
|
|
|
|
This is a multiline comment.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
/* It can be nested. */
|
|
|
|
|
*/
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Functions
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
fn main() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println(add(77, 33))
|
|
|
|
|
println(sub(100, 50))
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn add(x int, y int) int {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
return x + y
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-15 13:32:28 +03:00
|
|
|
|
fn sub(x int, y int) int {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
return x - y
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Again, the type comes after the argument's name.
|
|
|
|
|
|
|
|
|
|
Just like in Go and C, functions cannot be overloaded.
|
|
|
|
|
This simplifies the code and improves maintainability and readability.
|
|
|
|
|
|
|
|
|
|
Functions can be used before their declaration:
|
|
|
|
|
`add` and `sub` are declared after `main`, but can still be called from `main`.
|
2020-06-30 15:29:55 +03:00
|
|
|
|
This is true for all declarations in V and eliminates the need for header files
|
2019-11-21 04:36:55 +03:00
|
|
|
|
or thinking about the order of files and declarations.
|
|
|
|
|
|
2020-07-23 19:55:54 +03:00
|
|
|
|
### Returning multiple values
|
2020-04-16 03:01:55 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2019-11-21 04:36:55 +03:00
|
|
|
|
fn foo() (int, int) {
|
|
|
|
|
return 2, 3
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a, b := foo()
|
|
|
|
|
println(a) // 2
|
|
|
|
|
println(b) // 3
|
2020-07-14 20:24:59 +03:00
|
|
|
|
c, _ := foo() // ignore values using `_`
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-08-09 05:12:08 +03:00
|
|
|
|
### Variable number of arguments
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-08-09 05:12:08 +03:00
|
|
|
|
fn sum(a ...int) int {
|
|
|
|
|
mut total := 0
|
|
|
|
|
for x in a {
|
|
|
|
|
total += x
|
|
|
|
|
}
|
|
|
|
|
return total
|
|
|
|
|
}
|
|
|
|
|
println(sum()) // Output: 0
|
|
|
|
|
println(sum(1)) // 1
|
|
|
|
|
println(sum(2,3)) // 5
|
|
|
|
|
```
|
|
|
|
|
|
2020-07-23 19:55:54 +03:00
|
|
|
|
## Symbol visibility
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
pub fn public_function() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn private_function() {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-07-23 19:55:54 +03:00
|
|
|
|
Functions are private (not exported) by default.
|
2020-05-11 21:21:41 +03:00
|
|
|
|
To allow other modules to use them, prepend `pub`. The same applies
|
|
|
|
|
to constants and types.
|
|
|
|
|
|
2020-10-11 21:36:41 +03:00
|
|
|
|
Note: `pub` can only be used from a named module.
|
|
|
|
|
For information about creating a module, see [Modules](#modules).
|
|
|
|
|
|
2020-05-11 21:25:48 +03:00
|
|
|
|
## Variables
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
name := 'Bob'
|
|
|
|
|
age := 20
|
|
|
|
|
large_number := i64(9999999999)
|
|
|
|
|
println(name)
|
|
|
|
|
println(age)
|
|
|
|
|
println(large_number)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Variables are declared and initialized with `:=`. This is the only
|
|
|
|
|
way to declare variables in V. This means that variables always have an initial
|
|
|
|
|
value.
|
|
|
|
|
|
|
|
|
|
The variable's type is inferred from the value on the right hand side.
|
2020-06-30 15:29:55 +03:00
|
|
|
|
To choose a different type, use type conversion:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
the expression `T(v)` converts the value `v` to the
|
|
|
|
|
type `T`.
|
|
|
|
|
|
|
|
|
|
Unlike most other languages, V only allows defining variables in functions.
|
2020-07-23 19:55:54 +03:00
|
|
|
|
Global (module level) variables are not allowed. There's no global state in V
|
|
|
|
|
(see [Pure functions by default](#pure-functions-by-default) for details).
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-09-14 17:06:32 +03:00
|
|
|
|
For consistency across different code bases, all variable and function names
|
|
|
|
|
must use the `snake_case` style, as opposed to type names, which must use `PascalCase`.
|
|
|
|
|
|
2020-07-23 19:55:54 +03:00
|
|
|
|
### Mutable variables
|
2020-05-11 21:25:48 +03:00
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```v
|
2020-04-21 06:11:50 +03:00
|
|
|
|
mut age := 20
|
2019-11-21 04:36:55 +03:00
|
|
|
|
println(age)
|
|
|
|
|
age = 21
|
|
|
|
|
println(age)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
To change the value of the variable use `=`. In V, variables are
|
2020-10-20 21:14:56 +03:00
|
|
|
|
immutable by default.
|
|
|
|
|
To be able to change the value of the variable, you have to declare it with `mut`.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-04-21 06:11:50 +03:00
|
|
|
|
Try compiling the program above after removing `mut` from the first line.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-07-23 19:55:54 +03:00
|
|
|
|
### Initialization vs assignment
|
|
|
|
|
|
2020-09-07 14:50:12 +03:00
|
|
|
|
Note the (important) difference between `:=` and `=`.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
`:=` is used for declaring and initializing, `=` is used for assigning.
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v failcompile
|
2019-11-21 04:36:55 +03:00
|
|
|
|
fn main() {
|
|
|
|
|
age = 21
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
This code will not compile, because the variable `age` is not declared.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
All variables need to be declared in V.
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
fn main() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
age := 21
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-06-30 15:29:55 +03:00
|
|
|
|
### Declaration errors
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
In development mode the compiler will warn you that you haven't used the variable
|
|
|
|
|
(you'll get an "unused variable" warning).
|
|
|
|
|
In production mode (enabled by passing the `-prod` flag to v – `v -prod foo.v`)
|
|
|
|
|
it will not compile at all (like in Go).
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-04 21:34:05 +03:00
|
|
|
|
```v failcompile
|
2019-11-21 04:36:55 +03:00
|
|
|
|
fn main() {
|
|
|
|
|
a := 10
|
|
|
|
|
if true {
|
2020-12-01 18:09:33 +03:00
|
|
|
|
a := 20 // error: redefinition of `a`
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
2020-07-23 19:55:54 +03:00
|
|
|
|
// warning: unused variable `a`
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
Unlike most languages, variable shadowing is not allowed. Declaring a variable with a name
|
|
|
|
|
that is already used in a parent scope will cause a compilation error.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-01 18:09:33 +03:00
|
|
|
|
You can shadow imported modules though, as it is very useful in some situations:
|
|
|
|
|
```v ignore
|
|
|
|
|
import ui
|
|
|
|
|
import gg
|
|
|
|
|
|
|
|
|
|
fn draw(ctx &gg.Context) {
|
|
|
|
|
gg := ctx.parent.get_ui().gg
|
2020-12-30 04:27:05 +03:00
|
|
|
|
gg.draw_rect(10, 10, 100, 50)
|
2020-12-01 18:09:33 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
## Types
|
|
|
|
|
|
|
|
|
|
### Primitive types
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v ignore
|
2019-11-21 04:36:55 +03:00
|
|
|
|
bool
|
|
|
|
|
|
|
|
|
|
string
|
|
|
|
|
|
|
|
|
|
i8 i16 int i64 i128 (soon)
|
|
|
|
|
byte u16 u32 u64 u128 (soon)
|
|
|
|
|
|
|
|
|
|
rune // represents a Unicode code point
|
|
|
|
|
|
|
|
|
|
f32 f64
|
|
|
|
|
|
2020-05-27 06:42:48 +03:00
|
|
|
|
any_int, any_float // internal intermediate types of number literals
|
|
|
|
|
|
2020-06-11 08:00:14 +03:00
|
|
|
|
byteptr, voidptr, charptr, size_t // these are mostly used for C interoperability
|
2020-05-17 17:11:48 +03:00
|
|
|
|
|
|
|
|
|
any // similar to C's void* and Go's interface{}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Please note that unlike C and Go, `int` is always a 32 bit integer.
|
|
|
|
|
|
2020-09-15 16:50:35 +03:00
|
|
|
|
There is an exception to the rule that all operators
|
2020-05-27 06:42:48 +03:00
|
|
|
|
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
|
2020-06-02 18:00:14 +03:00
|
|
|
|
completely into the data range of the type on the other side.
|
2020-05-27 06:42:48 +03:00
|
|
|
|
These are the allowed possibilities:
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v ignore
|
2020-05-27 06:42:48 +03:00
|
|
|
|
i8 → i16 → int → i64
|
2020-06-02 18:00:14 +03:00
|
|
|
|
↘ ↘
|
|
|
|
|
f32 → f64
|
|
|
|
|
↗ ↗
|
2020-05-27 06:42:48 +03:00
|
|
|
|
byte → u16 → u32 → u64 ⬎
|
|
|
|
|
↘ ↘ ↘ ptr
|
|
|
|
|
i8 → i16 → int → i64 ⬏
|
|
|
|
|
```
|
|
|
|
|
An `int` value for example can be automatically promoted to `f64`
|
2020-06-28 03:19:52 +03:00
|
|
|
|
or `i64` but not to `f32` or `u32`. (`f32` would mean precision
|
2020-05-27 06:42:48 +03:00
|
|
|
|
loss for large values and `u32` would mean loss of the sign for
|
|
|
|
|
negative values).
|
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
### Strings
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2019-11-21 04:36:55 +03:00
|
|
|
|
name := 'Bob'
|
|
|
|
|
println(name.len)
|
2020-10-11 21:36:01 +03:00
|
|
|
|
println(name[0]) // indexing gives a byte B
|
|
|
|
|
println(name[1..3]) // slicing gives a string 'ob'
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-11 21:36:01 +03:00
|
|
|
|
windows_newline := '\r\n' // escape special characters like in C
|
|
|
|
|
assert windows_newline.len == 2
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
In V, a string is a read-only array of bytes. String data is encoded using UTF-8.
|
2020-10-11 21:36:01 +03:00
|
|
|
|
String values are immutable. You cannot mutate elements:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v failcompile
|
2020-10-11 21:36:01 +03:00
|
|
|
|
mut s := 'hello 🌎'
|
|
|
|
|
s[0] = `H` // not allowed
|
|
|
|
|
```
|
|
|
|
|
> error: cannot assign to `s[i]` since V strings are immutable
|
|
|
|
|
|
|
|
|
|
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 `
|
2020-09-14 17:18:10 +03:00
|
|
|
|
|
|
|
|
|
```v
|
2020-10-11 21:36:01 +03:00
|
|
|
|
rocket := `🚀`
|
|
|
|
|
assert 'aloha!'[0] == `a`
|
2020-09-14 17:18:10 +03:00
|
|
|
|
```
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2020-10-11 21:36:01 +03:00
|
|
|
|
For raw strings, prepend `r`. Raw strings are not escaped:
|
2020-05-02 01:43:59 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-10-11 21:36:01 +03:00
|
|
|
|
s := r'hello\nworld'
|
|
|
|
|
println(s) // "hello\nworld"
|
2020-05-02 01:43:59 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-10-11 21:36:01 +03:00
|
|
|
|
### String interpolation
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-11 21:36:01 +03:00
|
|
|
|
Basic interpolation syntax is pretty simple - use `$` before a variable name.
|
|
|
|
|
The variable will be converted to a string and embedded into the literal:
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-10-11 21:36:01 +03:00
|
|
|
|
name := 'Bob'
|
|
|
|
|
println('Hello, $name!') // Hello, Bob!
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
2020-10-11 21:36:01 +03:00
|
|
|
|
It also works with fields: `'age = $user.age'`.
|
|
|
|
|
If you need more complex expressions, use `${}`: `'can register = ${user.age > 13}'`.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
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`.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-10-11 21:36:01 +03:00
|
|
|
|
x := 123.4567
|
|
|
|
|
println('x = ${x:4.2f}')
|
|
|
|
|
println('[${x:10}]') // pad with spaces on the left
|
|
|
|
|
println('[${int(x):-10}]') // pad with spaces on the right
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-10-11 21:36:01 +03:00
|
|
|
|
### String operators
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-11-03 03:04:14 +03:00
|
|
|
|
name := 'Bob'
|
2020-10-11 21:36:01 +03:00
|
|
|
|
bobby := name + 'by' // + is used to concatenate strings
|
|
|
|
|
println(bobby) // "Bobby"
|
|
|
|
|
|
|
|
|
|
mut s := 'hello '
|
|
|
|
|
s += 'world' // `+=` is used to append to a string
|
|
|
|
|
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:
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v failcompile
|
2020-10-11 21:36:01 +03:00
|
|
|
|
age := 10
|
|
|
|
|
println('age = ' + age) // not allowed
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
2020-10-11 21:36:01 +03:00
|
|
|
|
> error: infix expr: cannot use `int` (right expression) as `string`
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-11 21:36:01 +03:00
|
|
|
|
We have to either convert `age` to a `string`:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
```v
|
2020-11-03 03:04:14 +03:00
|
|
|
|
age := 11
|
2020-10-11 21:36:01 +03:00
|
|
|
|
println('age = ' + age.str())
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-10-11 21:36:01 +03:00
|
|
|
|
or use string interpolation (preferred):
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
```v
|
2020-11-03 03:04:14 +03:00
|
|
|
|
age := 12
|
2020-10-11 21:36:01 +03:00
|
|
|
|
println('age = $age')
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-07-14 11:10:36 +03:00
|
|
|
|
### Numbers
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
a := 123
|
|
|
|
|
```
|
2020-07-14 16:46:13 +03:00
|
|
|
|
|
2020-07-14 11:10:36 +03:00
|
|
|
|
This will assign the value of 123 to `a`. By default `a` will have the
|
2020-07-14 11:38:40 +03:00
|
|
|
|
type `int`.
|
2020-07-14 11:10:36 +03:00
|
|
|
|
|
2020-07-14 20:26:14 +03:00
|
|
|
|
You can also use hexadecimal, binary or octal notation for integer literals:
|
2020-07-14 16:46:13 +03:00
|
|
|
|
|
2020-07-14 11:10:36 +03:00
|
|
|
|
```v
|
|
|
|
|
a := 0x7B
|
2020-07-14 20:26:14 +03:00
|
|
|
|
b := 0b01111011
|
|
|
|
|
c := 0o173
|
2020-07-14 11:10:36 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-07-14 20:26:14 +03:00
|
|
|
|
All of these will be assigned the same value, 123. They will all have type
|
|
|
|
|
`int`, no matter what notation you used.
|
2020-07-14 11:10:36 +03:00
|
|
|
|
|
2020-07-14 16:46:13 +03:00
|
|
|
|
V also supports writing numbers with `_` as separator:
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-07-14 16:46:13 +03:00
|
|
|
|
num := 1_000_000 // same as 1000000
|
|
|
|
|
three := 0b0_11 // same as 0b11
|
|
|
|
|
float_num := 3_122.55 // same as 3122.55
|
|
|
|
|
hexa := 0xF_F // same as 255
|
|
|
|
|
oct := 0o17_3 // same as 0o173
|
|
|
|
|
```
|
2020-07-14 11:10:36 +03:00
|
|
|
|
|
|
|
|
|
If you want a different type of integer, you can use casting:
|
2020-07-14 16:46:13 +03:00
|
|
|
|
|
2020-07-14 11:10:36 +03:00
|
|
|
|
```v
|
|
|
|
|
a := i64(123)
|
|
|
|
|
b := byte(42)
|
|
|
|
|
c := i16(12345)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Assigning floating point numbers works the same way:
|
2020-07-14 16:46:13 +03:00
|
|
|
|
|
2020-07-14 11:10:36 +03:00
|
|
|
|
```v
|
|
|
|
|
f := 1.0
|
|
|
|
|
f1 := f64(3.14)
|
|
|
|
|
f2 := f32(3.14)
|
|
|
|
|
```
|
2020-08-12 20:14:13 +03:00
|
|
|
|
If you do not specify the type explicitly, by default float literals
|
2020-07-14 11:10:36 +03:00
|
|
|
|
will have the type of `f64`.
|
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
### Arrays
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-04-21 06:11:50 +03:00
|
|
|
|
mut nums := [1, 2, 3]
|
2019-11-21 04:36:55 +03:00
|
|
|
|
println(nums) // "[1, 2, 3]"
|
|
|
|
|
println(nums[1]) // "2"
|
2020-06-30 15:37:02 +03:00
|
|
|
|
nums[1] = 5
|
|
|
|
|
println(nums) // "[1, 5, 3]"
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-02 06:06:43 +03:00
|
|
|
|
println(nums[0..2]) // slicing gives an array "[1, 5]"
|
|
|
|
|
|
2020-06-30 15:37:02 +03:00
|
|
|
|
println(nums.len) // "3"
|
|
|
|
|
nums = [] // The array is now empty
|
|
|
|
|
println(nums.len) // "0"
|
|
|
|
|
|
|
|
|
|
// Declare an empty array:
|
|
|
|
|
users := []int{}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The type of an array is determined by the first element:
|
|
|
|
|
* `[1, 2, 3]` is an array of ints (`[]int`).
|
|
|
|
|
* `['a', 'b']` is an array of strings (`[]string`).
|
|
|
|
|
|
2020-12-16 20:23:00 +03:00
|
|
|
|
The user can explicitly specify the type for the first element: `[byte(16), 32, 64, 128]`.
|
2020-10-20 21:14:56 +03:00
|
|
|
|
V arrays are homogeneous (all elements must have the same type).
|
|
|
|
|
This means that code like `[1, 'a']` will not compile.
|
2020-06-30 15:37:02 +03:00
|
|
|
|
|
2020-07-26 21:29:51 +03:00
|
|
|
|
The `.len` field returns the length of the array. Note that it's a read-only field,
|
2020-06-30 15:37:02 +03:00
|
|
|
|
and it can't be modified by the user. Exported fields are read-only by default in V.
|
|
|
|
|
See [Access modifiers](#access-modifiers).
|
|
|
|
|
|
2020-07-26 21:29:51 +03:00
|
|
|
|
#### Array operations
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-06-30 15:37:02 +03:00
|
|
|
|
mut nums := [1, 2, 3]
|
2019-11-21 04:36:55 +03:00
|
|
|
|
nums << 4
|
|
|
|
|
println(nums) // "[1, 2, 3, 4]"
|
|
|
|
|
|
2020-06-30 15:37:02 +03:00
|
|
|
|
// append array
|
2019-11-21 04:36:55 +03:00
|
|
|
|
nums << [5, 6, 7]
|
|
|
|
|
println(nums) // "[1, 2, 3, 4, 5, 6, 7]"
|
|
|
|
|
|
2020-04-21 06:11:50 +03:00
|
|
|
|
mut names := ['John']
|
2019-11-21 04:36:55 +03:00
|
|
|
|
names << 'Peter'
|
|
|
|
|
names << 'Sam'
|
|
|
|
|
// names << 10 <-- This will not compile. `names` is an array of strings.
|
|
|
|
|
println(names.len) // "3"
|
|
|
|
|
println('Alex' in names) // "false"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`<<` is an operator that appends a value to the end of the array.
|
|
|
|
|
It can also append an entire array.
|
|
|
|
|
|
2020-06-30 15:37:02 +03:00
|
|
|
|
`val in array` returns true if the array contains `val`. See [`in` operator](#in-operator).
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-07-26 21:29:51 +03:00
|
|
|
|
#### Initializing array properties
|
2020-07-07 12:55:22 +03:00
|
|
|
|
|
|
|
|
|
During initialization you can specify the capacity of the array (`cap`), its initial length (`len`),
|
2020-07-26 21:29:51 +03:00
|
|
|
|
and the default element (`init`):
|
|
|
|
|
|
|
|
|
|
```v
|
2020-12-06 00:54:41 +03:00
|
|
|
|
arr := []int{len: 5, init: -1}
|
|
|
|
|
// `[-1, -1, -1, -1, -1]`
|
2020-07-26 21:29:51 +03:00
|
|
|
|
```
|
2020-07-07 12:55:22 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
Setting the capacity improves performance of insertions,
|
|
|
|
|
as it reduces the number of reallocations needed:
|
2020-07-07 12:55:22 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-07-22 17:28:09 +03:00
|
|
|
|
mut numbers := []int{ cap: 1000 }
|
2020-07-26 21:29:51 +03:00
|
|
|
|
println(numbers.len) // 0
|
|
|
|
|
// Now appending elements won't reallocate
|
2020-07-07 12:55:22 +03:00
|
|
|
|
for i in 0 .. 1000 {
|
|
|
|
|
numbers << i
|
|
|
|
|
}
|
|
|
|
|
```
|
2020-07-26 21:29:51 +03:00
|
|
|
|
Note: The above code uses a [range `for`](#range-for) statement.
|
2020-07-07 12:55:22 +03:00
|
|
|
|
|
2020-06-30 15:37:02 +03:00
|
|
|
|
#### Array methods
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
All arrays can be easily printed with `println(arr)` and converted to a string
|
|
|
|
|
with `s := arr.str()`.
|
|
|
|
|
|
2020-12-14 02:37:09 +03:00
|
|
|
|
Copying the data from the array is done with `.clone()`:
|
|
|
|
|
|
|
|
|
|
```v nofmt
|
|
|
|
|
nums := [1, 2, 3]
|
|
|
|
|
nums_copy := nums.clone()
|
|
|
|
|
```
|
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
Arrays can be efficiently filtered and mapped with the `.filter()` and
|
2019-11-21 04:36:55 +03:00
|
|
|
|
`.map()` methods:
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2019-11-21 04:36:55 +03:00
|
|
|
|
nums := [1, 2, 3, 4, 5, 6]
|
|
|
|
|
even := nums.filter(it % 2 == 0)
|
|
|
|
|
println(even) // [2, 4, 6]
|
2020-12-02 16:41:31 +03:00
|
|
|
|
// filter can accept anonymous functions
|
|
|
|
|
even_fn := nums.filter(fn (x int) bool {
|
|
|
|
|
return x % 2 == 0
|
|
|
|
|
})
|
|
|
|
|
println(even_fn)
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
words := ['hello', 'world']
|
|
|
|
|
upper := words.map(it.to_upper())
|
|
|
|
|
println(upper) // ['HELLO', 'WORLD']
|
2020-12-02 16:41:31 +03:00
|
|
|
|
// map can also accept anonymous functions
|
|
|
|
|
upper_fn := words.map(fn (w string) string {
|
|
|
|
|
return w.to_upper()
|
|
|
|
|
})
|
|
|
|
|
println(upper_fn) // ['HELLO', 'WORLD']
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-05-07 19:01:09 +03:00
|
|
|
|
`it` is a builtin variable which refers to element currently being processed in filter/map methods.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-08-05 02:46:04 +03:00
|
|
|
|
#### Multidimensional Arrays
|
|
|
|
|
|
|
|
|
|
Arrays can have more than one dimension.
|
|
|
|
|
|
|
|
|
|
2d array example:
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-08-05 02:46:04 +03:00
|
|
|
|
mut a := [][]int{len:2, init: []int{len:3}}
|
|
|
|
|
a[0][1] = 2
|
|
|
|
|
println(a) // [[0, 2, 0], [0, 0, 0]]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
3d array example:
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-08-05 02:46:04 +03:00
|
|
|
|
mut a := [][][]int{len:2, init: [][]int{len:3, init: []int{len:2}}}
|
|
|
|
|
a[0][1][1] = 2
|
|
|
|
|
println(a) // [[[0, 0], [0, 2], [0, 0]], [[0, 0], [0, 0], [0, 0]]]
|
|
|
|
|
```
|
|
|
|
|
|
2020-08-12 20:14:13 +03:00
|
|
|
|
#### 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.
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-08-12 20:14:13 +03:00
|
|
|
|
mut numbers := [1, 3, 2]
|
|
|
|
|
numbers.sort() // 1, 2, 3
|
|
|
|
|
numbers.sort(a > b) // 3, 2, 1
|
|
|
|
|
```
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-08-12 20:14:13 +03:00
|
|
|
|
struct User { age int name string }
|
2020-10-21 12:57:58 +03:00
|
|
|
|
mut users := [User{21, 'Bob'}, User{20, 'Zarkon'}, User{25, 'Alice'}]
|
2020-08-12 20:14:13 +03:00
|
|
|
|
users.sort(a.age < b.age) // sort by User.age int field
|
|
|
|
|
users.sort(a.name > b.name) // reverse sort by User.name string field
|
|
|
|
|
```
|
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
### Maps
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-04-21 06:11:50 +03:00
|
|
|
|
mut m := map[string]int // Only maps with string keys are allowed for now
|
2019-11-21 04:36:55 +03:00
|
|
|
|
m['one'] = 1
|
|
|
|
|
m['two'] = 2
|
|
|
|
|
println(m['one']) // "1"
|
|
|
|
|
println(m['bad_key']) // "0"
|
|
|
|
|
println('bad_key' in m) // Use `in` to detect whether such key exists
|
|
|
|
|
m.delete('two')
|
|
|
|
|
|
2020-04-26 18:19:08 +03:00
|
|
|
|
// Short syntax
|
2019-11-21 04:36:55 +03:00
|
|
|
|
numbers := {
|
2020-06-26 14:07:57 +03:00
|
|
|
|
'one': 1
|
2020-01-16 17:29:00 +03:00
|
|
|
|
'two': 2
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-07-23 19:55:54 +03:00
|
|
|
|
## Module imports
|
2020-07-18 22:34:38 +03:00
|
|
|
|
|
2020-10-11 21:36:41 +03:00
|
|
|
|
For information about creating a module, see [Modules](#modules).
|
2020-07-18 22:34:38 +03:00
|
|
|
|
|
2020-10-11 21:36:41 +03:00
|
|
|
|
Modules can be imported using the `import` keyword:
|
2020-06-26 20:36:40 +03:00
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
fn main() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
// read text from stdin
|
|
|
|
|
name := os.input('Enter your name: ')
|
|
|
|
|
println('Hello, $name!')
|
2020-06-26 20:36:40 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
2020-10-15 13:32:28 +03:00
|
|
|
|
This program can use any public definitions from the `os` module, such
|
2020-10-11 21:36:41 +03:00
|
|
|
|
as the `input` function. See the [standard library](https://modules.vlang.io/)
|
|
|
|
|
documentation for a list of common modules and their public symbols.
|
2020-06-26 20:36:40 +03:00
|
|
|
|
|
2020-10-11 21:36:41 +03:00
|
|
|
|
By default, you have to specify the module prefix every time you call an external function.
|
|
|
|
|
This may seem verbose at first, but it makes code much more readable
|
|
|
|
|
and easier to understand - it's always clear which function from
|
|
|
|
|
which module is being called. This is especially useful in large code bases.
|
|
|
|
|
|
|
|
|
|
### Selective imports
|
|
|
|
|
|
|
|
|
|
You can also import specific functions and types from modules directly:
|
2020-07-18 22:34:38 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-07-18 22:34:38 +03:00
|
|
|
|
import os { input }
|
|
|
|
|
import crypto.sha256 { sum }
|
|
|
|
|
import time { Time }
|
|
|
|
|
```
|
2020-10-11 21:36:41 +03:00
|
|
|
|
Note: This is not allowed for constants - they must always be prefixed.
|
|
|
|
|
|
|
|
|
|
You can import several specific symbols at once:
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
import os { input, user_os }
|
|
|
|
|
|
|
|
|
|
name := input('Enter your name: ')
|
|
|
|
|
println('Name: $name')
|
|
|
|
|
os := user_os()
|
|
|
|
|
println('Your OS is ${os}.')
|
|
|
|
|
```
|
2020-07-18 22:34:38 +03:00
|
|
|
|
|
2020-07-23 19:55:54 +03:00
|
|
|
|
### Module import aliasing
|
2020-07-18 22:34:38 +03:00
|
|
|
|
|
|
|
|
|
Any imported module name can be aliased using the `as` keyword:
|
|
|
|
|
|
|
|
|
|
NOTE: this example will not compile unless you have created `mymod/sha256.v`
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v failcompile
|
2020-07-18 22:34:38 +03:00
|
|
|
|
import crypto.sha256
|
|
|
|
|
import mymod.sha256 as mysha256
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
v_hash := sha256.sum('hi'.bytes()).hex()
|
|
|
|
|
my_hash := mysha256.sum('hi'.bytes()).hex()
|
|
|
|
|
assert my_hash == v_hash
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You cannot alias an imported function or type.
|
|
|
|
|
However, you _can_ redeclare a type.
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
import time
|
2020-12-25 18:19:00 +03:00
|
|
|
|
import math
|
2020-07-18 22:34:38 +03:00
|
|
|
|
|
2020-09-25 13:02:32 +03:00
|
|
|
|
type MyTime = time.Time
|
2020-07-18 22:34:38 +03:00
|
|
|
|
|
2020-09-16 23:21:02 +03:00
|
|
|
|
fn (mut t MyTime) century() int {
|
2020-12-25 18:19:00 +03:00
|
|
|
|
return int(1.0 + math.trunc(f64(t.year) * 0.009999794661191))
|
2020-09-16 23:21:02 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-18 22:34:38 +03:00
|
|
|
|
fn main() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
mut my_time := MyTime{
|
|
|
|
|
year: 2020
|
|
|
|
|
month: 12
|
|
|
|
|
day: 25
|
|
|
|
|
}
|
|
|
|
|
println(time.new_time(my_time).utc_string())
|
|
|
|
|
println('Century: $my_time.century()')
|
2020-07-18 22:34:38 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
2020-06-26 20:36:40 +03:00
|
|
|
|
|
2020-07-23 19:55:54 +03:00
|
|
|
|
## Statements & expressions
|
2020-06-26 20:36:40 +03:00
|
|
|
|
|
|
|
|
|
### If
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
a := 10
|
|
|
|
|
b := 20
|
|
|
|
|
if a < b {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println('$a < $b')
|
2019-11-21 04:36:55 +03:00
|
|
|
|
} else if a > b {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println('$a > $b')
|
2019-11-21 04:36:55 +03:00
|
|
|
|
} else {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println('$a == $b')
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`if` statements are pretty straightforward and similar to most other languages.
|
2020-10-20 21:14:56 +03:00
|
|
|
|
Unlike other C-like languages,
|
|
|
|
|
there are no parentheses surrounding the condition and the braces are always required.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
`if` can be used as an expression:
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
num := 777
|
2020-12-06 00:54:41 +03:00
|
|
|
|
s := if num % 2 == 0 { 'even' } else { 'odd' }
|
|
|
|
|
println(s)
|
|
|
|
|
// "odd"
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-11-27 23:24:35 +03:00
|
|
|
|
#### Type checks and casts
|
|
|
|
|
You can check the current type of a sum type using `is` and its negated form `!is`.
|
|
|
|
|
|
|
|
|
|
You can do it either in an `if`:
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-07-09 18:14:14 +03:00
|
|
|
|
struct Abc {
|
|
|
|
|
val string
|
|
|
|
|
}
|
|
|
|
|
struct Xyz {
|
|
|
|
|
foo string
|
|
|
|
|
}
|
|
|
|
|
type Alphabet = Abc | Xyz
|
|
|
|
|
|
|
|
|
|
x := Alphabet(Abc{'test'}) // sum type
|
|
|
|
|
if x is Abc {
|
2020-11-27 23:24:35 +03:00
|
|
|
|
// x is automatically casted to Abc and can be used here
|
2020-07-09 18:14:14 +03:00
|
|
|
|
println(x)
|
|
|
|
|
}
|
2020-11-27 23:24:35 +03:00
|
|
|
|
if x !is Abc {
|
|
|
|
|
println('Not Abc')
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
or using `match`:
|
|
|
|
|
```v oksyntax
|
|
|
|
|
match x {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
Abc {
|
|
|
|
|
// x is automatically casted to Abc and can be used here
|
|
|
|
|
println(x)
|
|
|
|
|
}
|
|
|
|
|
Xyz {
|
|
|
|
|
// x is automatically casted to Xyz and can be used here
|
|
|
|
|
println(x)
|
|
|
|
|
}
|
2020-11-27 23:24:35 +03:00
|
|
|
|
}
|
2020-07-09 18:14:14 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-11-27 23:24:35 +03:00
|
|
|
|
This works also with struct fields:
|
|
|
|
|
```v
|
|
|
|
|
struct MyStruct {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
x int
|
2020-11-27 23:24:35 +03:00
|
|
|
|
}
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2020-11-27 23:24:35 +03:00
|
|
|
|
struct MyStruct2 {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
y string
|
2020-11-27 23:24:35 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
type MySumType = MyStruct | MyStruct2
|
2020-11-27 23:24:35 +03:00
|
|
|
|
|
|
|
|
|
struct Abc {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
bar MySumType
|
2020-11-27 23:24:35 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
x := Abc{
|
2020-12-06 00:54:41 +03:00
|
|
|
|
bar: MyStruct{123} // MyStruct will be converted to MySumType type automatically
|
2020-11-27 23:24:35 +03:00
|
|
|
|
}
|
|
|
|
|
if x.bar is MyStruct {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
// x.bar is automatically casted
|
|
|
|
|
println(x.bar)
|
2020-11-27 23:24:35 +03:00
|
|
|
|
}
|
|
|
|
|
match x.bar {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
MyStruct {
|
|
|
|
|
// x.bar is automatically casted
|
|
|
|
|
println(x.bar)
|
|
|
|
|
}
|
|
|
|
|
else {}
|
2020-11-27 23:24:35 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Mutable variables can change, and doing a cast would be unsafe.
|
|
|
|
|
However, sometimes it's needed to have a type cast despite of mutability.
|
|
|
|
|
In this case the developer has to mark the expression with a `mut` keyword
|
|
|
|
|
to tell the compiler that you're aware of what you're doing.
|
|
|
|
|
|
|
|
|
|
It works like this:
|
|
|
|
|
```v oksyntax
|
|
|
|
|
mut x := MySumType(MyStruct{123})
|
|
|
|
|
if mut x is MyStruct {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
// x is casted to MyStruct even it's mutable
|
|
|
|
|
// without the mut keyword that wouldn't work
|
|
|
|
|
println(x)
|
2020-11-27 23:24:35 +03:00
|
|
|
|
}
|
|
|
|
|
// same with match
|
|
|
|
|
match mut x {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
MyStruct {
|
|
|
|
|
// x is casted to MyStruct even it's mutable
|
|
|
|
|
// without the mut keyword that wouldn't work
|
|
|
|
|
println(x)
|
|
|
|
|
}
|
2020-07-09 18:14:14 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
### In operator
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
`in` allows to check whether an array or a map contains an element.
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2019-11-21 04:36:55 +03:00
|
|
|
|
nums := [1, 2, 3]
|
|
|
|
|
println(1 in nums) // true
|
|
|
|
|
|
|
|
|
|
m := {'one': 1, 'two': 2}
|
|
|
|
|
println('one' in m) // true
|
|
|
|
|
```
|
|
|
|
|
|
2020-10-25 15:55:57 +03:00
|
|
|
|
It's also useful for writing boolean expressions that are clearer and more compact:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
```v
|
2020-12-06 00:54:41 +03:00
|
|
|
|
enum Token {
|
|
|
|
|
plus
|
|
|
|
|
minus
|
|
|
|
|
div
|
|
|
|
|
mult
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Parser {
|
|
|
|
|
token Token
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
parser := Parser{}
|
|
|
|
|
if parser.token == .plus || parser.token == .minus || parser.token == .div || parser.token == .mult {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
if parser.token in [.plus, .minus, .div, .mult] {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
// ...
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
V optimizes such expressions,
|
|
|
|
|
so both `if` statements above produce the same machine code and no arrays are created.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
### For loop
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-07-23 19:55:35 +03:00
|
|
|
|
V has only one looping keyword: `for`, with several forms.
|
|
|
|
|
|
|
|
|
|
#### Array `for`
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2019-11-21 04:36:55 +03:00
|
|
|
|
numbers := [1, 2, 3, 4, 5]
|
|
|
|
|
for num in numbers {
|
|
|
|
|
println(num)
|
|
|
|
|
}
|
|
|
|
|
names := ['Sam', 'Peter']
|
|
|
|
|
for i, name in names {
|
|
|
|
|
println('$i) $name') // Output: 0) Sam
|
2020-07-23 19:55:35 +03:00
|
|
|
|
} // 1) Peter
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-07-23 19:55:35 +03:00
|
|
|
|
The `for value in arr` form is used for going through elements of an array.
|
|
|
|
|
If an index is required, an alternative form `for index, value in arr` can be used.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
Note, that the value is read-only.
|
|
|
|
|
If you need to modify the array while looping, you have to use indexing:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-06-30 15:29:55 +03:00
|
|
|
|
mut numbers := [0, 1, 2]
|
|
|
|
|
for i, _ in numbers {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
numbers[i]++
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
2020-06-30 15:29:55 +03:00
|
|
|
|
println(numbers) // [1, 2, 3]
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
2020-06-30 15:29:55 +03:00
|
|
|
|
When an identifier is just a single underscore, it is ignored.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-07-31 19:08:12 +03:00
|
|
|
|
#### Map `for`
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-07-31 19:08:12 +03:00
|
|
|
|
m := {'one':1, 'two':2}
|
|
|
|
|
for key, value in m {
|
|
|
|
|
println("$key -> $value") // Output: one -> 1
|
|
|
|
|
} // two -> 2
|
|
|
|
|
```
|
|
|
|
|
|
2020-12-23 20:03:56 +03:00
|
|
|
|
Either key or value can be ignored by using a single underscore as the identifier.
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-07-31 19:08:12 +03:00
|
|
|
|
m := {'one':1, 'two':2}
|
|
|
|
|
|
|
|
|
|
// iterate over keys
|
|
|
|
|
for key, _ in m {
|
|
|
|
|
println(key) // Output: one
|
|
|
|
|
} // two
|
|
|
|
|
|
|
|
|
|
// iterate over values
|
|
|
|
|
for _, value in m {
|
|
|
|
|
println(value) // Output: 1
|
|
|
|
|
} // 2
|
|
|
|
|
```
|
|
|
|
|
|
2020-07-23 19:55:35 +03:00
|
|
|
|
#### Range `for`
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
// Prints '01234'
|
2020-12-06 00:54:41 +03:00
|
|
|
|
for i in 0 .. 5 {
|
|
|
|
|
print(i)
|
2020-07-23 19:55:35 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
2020-08-12 20:14:13 +03:00
|
|
|
|
`low..high` means an *exclusive* range, which represents all values
|
2020-07-23 19:55:35 +03:00
|
|
|
|
from `low` up to *but not including* `high`.
|
|
|
|
|
|
|
|
|
|
#### Condition `for`
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-04-21 06:11:50 +03:00
|
|
|
|
mut sum := 0
|
|
|
|
|
mut i := 0
|
2019-11-21 04:36:55 +03:00
|
|
|
|
for i <= 100 {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
sum += i
|
|
|
|
|
i++
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
println(sum) // "5050"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This form of the loop is similar to `while` loops in other languages.
|
|
|
|
|
The loop will stop iterating once the boolean condition evaluates to false.
|
|
|
|
|
Again, there are no parentheses surrounding the condition, and the braces are always required.
|
|
|
|
|
|
2020-07-23 19:55:35 +03:00
|
|
|
|
#### Bare `for`
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-04-21 06:11:50 +03:00
|
|
|
|
mut num := 0
|
2019-11-21 04:36:55 +03:00
|
|
|
|
for {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
num += 2
|
|
|
|
|
if num >= 10 {
|
|
|
|
|
break
|
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
println(num) // "10"
|
|
|
|
|
```
|
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
The condition can be omitted, resulting in an infinite loop.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-07-23 19:55:35 +03:00
|
|
|
|
#### C `for`
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```v
|
2020-07-23 19:55:35 +03:00
|
|
|
|
for i := 0; i < 10; i += 2 {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
// Don't print 6
|
|
|
|
|
if i == 6 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
println(i)
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Finally, there's the traditional C style `for` loop. It's safer than the `while` form
|
|
|
|
|
because with the latter it's easy to forget to update the counter and get
|
|
|
|
|
stuck in an infinite loop.
|
|
|
|
|
|
2020-04-21 06:11:50 +03:00
|
|
|
|
Here `i` doesn't need to be declared with `mut` since it's always going to be mutable by definition.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-11-20 16:11:56 +03:00
|
|
|
|
#### Labelled break & continue
|
|
|
|
|
|
|
|
|
|
`break` and `continue` control the innermost `for` loop by default.
|
2020-12-01 18:09:33 +03:00
|
|
|
|
You can also use `break` and `continue` followed by a label name to refer to an outer `for`
|
2020-11-20 16:11:56 +03:00
|
|
|
|
loop:
|
|
|
|
|
|
|
|
|
|
```v
|
2020-12-06 00:54:41 +03:00
|
|
|
|
outer: for i := 4; true; i++ {
|
2020-11-20 16:11:56 +03:00
|
|
|
|
println(i)
|
|
|
|
|
for {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
if i < 7 {
|
|
|
|
|
continue outer
|
|
|
|
|
} else {
|
|
|
|
|
break outer
|
|
|
|
|
}
|
2020-11-20 16:11:56 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
The label must immediately precede the outer loop.
|
|
|
|
|
The above code prints:
|
|
|
|
|
```
|
|
|
|
|
4
|
|
|
|
|
5
|
|
|
|
|
6
|
|
|
|
|
7
|
|
|
|
|
```
|
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
### Match
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
os := 'windows'
|
|
|
|
|
print('V is running on ')
|
|
|
|
|
match os {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
'darwin' { println('macOS.') }
|
|
|
|
|
'linux' { println('Linux.') }
|
|
|
|
|
else { println(os) }
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
2020-07-08 16:56:49 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
A match statement is a shorter way to write a sequence of `if - else` statements.
|
|
|
|
|
When a matching branch is found, the following statement block will be run.
|
|
|
|
|
The else branch will be run when no other branches match.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-07-08 16:56:49 +03:00
|
|
|
|
```v
|
2020-01-16 17:29:00 +03:00
|
|
|
|
number := 2
|
2019-11-21 04:36:55 +03:00
|
|
|
|
s := match number {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
1 { 'one' }
|
|
|
|
|
2 { 'two' }
|
|
|
|
|
else { 'many' }
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-12-25 18:28:12 +03:00
|
|
|
|
A match expression returns the value of the final expression from the matching branch.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
enum Color {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
red
|
|
|
|
|
blue
|
|
|
|
|
green
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_red_or_blue(c Color) bool {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
return match c {
|
|
|
|
|
.red, .blue { true } // comma can be used to test multiple values
|
|
|
|
|
.green { false }
|
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
A match statement can also be used to branch on the variants of an `enum`
|
2020-07-08 16:56:49 +03:00
|
|
|
|
by using the shorthand `.variant_here` syntax. An `else` branch is not allowed
|
|
|
|
|
when all the branches are exhaustive.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-07-22 03:31:32 +03:00
|
|
|
|
```v
|
|
|
|
|
c := `v`
|
|
|
|
|
typ := match c {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
`0`...`9` { 'digit' }
|
|
|
|
|
`A`...`Z` { 'uppercase' }
|
|
|
|
|
`a`...`z` { 'lowercase' }
|
|
|
|
|
else { 'other' }
|
2020-07-22 03:31:32 +03:00
|
|
|
|
}
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println(typ)
|
|
|
|
|
// 'lowercase'
|
2020-07-22 03:31:32 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You can also use ranges as `match` patterns. If the value falls within the range
|
|
|
|
|
of a branch, that branch will be executed.
|
|
|
|
|
|
|
|
|
|
Note that the ranges use `...` (three dots) rather than `..` (two dots). This is
|
|
|
|
|
because the range is *inclusive* of the last element, rather than exclusive
|
|
|
|
|
(as `..` ranges are). Using `..` in a match branch will throw an error.
|
|
|
|
|
|
2020-10-03 16:30:58 +03:00
|
|
|
|
Note: `match` as an expression is not usable in `for` loop and `if` statements.
|
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
### Defer
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
A defer statement defers the execution of a block of statements
|
|
|
|
|
until the surrounding function returns.
|
2020-06-26 20:36:40 +03:00
|
|
|
|
|
|
|
|
|
```v
|
2020-11-03 03:04:14 +03:00
|
|
|
|
import os
|
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
fn read_log() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
mut ok := false
|
|
|
|
|
mut f := os.open('log.txt') or { panic(err) }
|
|
|
|
|
defer {
|
|
|
|
|
f.close()
|
|
|
|
|
}
|
|
|
|
|
// ...
|
|
|
|
|
if !ok {
|
|
|
|
|
// defer statement will be called here, the file will be closed
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// ...
|
|
|
|
|
// defer statement will be called here, the file will be closed
|
2020-06-26 20:36:40 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
## Structs
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2019-11-21 04:36:55 +03:00
|
|
|
|
struct Point {
|
|
|
|
|
x int
|
|
|
|
|
y int
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-08 17:02:35 +03:00
|
|
|
|
mut p := Point{
|
2019-11-21 04:36:55 +03:00
|
|
|
|
x: 10
|
|
|
|
|
y: 20
|
|
|
|
|
}
|
2020-01-16 17:29:00 +03:00
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
println(p.x) // Struct fields are accessed using a dot
|
2020-07-08 17:02:35 +03:00
|
|
|
|
|
|
|
|
|
// Alternative literal syntax for structs with 3 fields or fewer
|
|
|
|
|
p = Point{10, 20}
|
|
|
|
|
assert p.x == 10
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-07-23 19:55:54 +03:00
|
|
|
|
### Heap structs
|
2020-05-16 16:19:48 +03:00
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
Structs are allocated on the stack. To allocate a struct on the heap
|
|
|
|
|
and get a reference to it, use the `&` prefix:
|
|
|
|
|
|
|
|
|
|
```v
|
2020-11-03 03:04:14 +03:00
|
|
|
|
struct Point {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
x int
|
|
|
|
|
y int
|
2020-11-03 03:04:14 +03:00
|
|
|
|
}
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
p := &Point{10, 10}
|
|
|
|
|
// References have the same syntax for accessing fields
|
|
|
|
|
println(p.x)
|
|
|
|
|
```
|
|
|
|
|
|
2020-07-23 19:55:54 +03:00
|
|
|
|
The type of `p` is `&Point`. It's a [reference](#references) to `Point`.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
References are similar to Go pointers and C++ references.
|
|
|
|
|
|
2020-07-23 19:55:54 +03:00
|
|
|
|
### Embedded structs
|
2020-05-16 16:17:27 +03:00
|
|
|
|
|
2020-05-16 16:19:48 +03:00
|
|
|
|
V doesn't allow subclassing, but it supports embedded structs:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-11-04 01:08:03 +03:00
|
|
|
|
```v
|
2020-11-12 15:50:37 +03:00
|
|
|
|
struct Widget {
|
|
|
|
|
mut:
|
|
|
|
|
x int
|
|
|
|
|
y int
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
struct Button {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
Widget
|
2020-12-23 21:12:49 +03:00
|
|
|
|
title string
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
mut button := Button{
|
|
|
|
|
title: 'Click me'
|
|
|
|
|
}
|
2020-11-12 15:50:37 +03:00
|
|
|
|
button.x = 3
|
|
|
|
|
```
|
|
|
|
|
Without embedding we'd have to name the `Widget` field and do:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-11-12 15:50:37 +03:00
|
|
|
|
```v oksyntax
|
|
|
|
|
button.widget.x = 3
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-07-23 19:55:54 +03:00
|
|
|
|
### Default field values
|
2020-05-16 16:19:48 +03:00
|
|
|
|
|
2020-05-16 16:17:27 +03:00
|
|
|
|
```v
|
|
|
|
|
struct Foo {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
n int // n is 0 by default
|
|
|
|
|
s string // s is '' by default
|
|
|
|
|
a []int // a is `[]int{}` by default
|
|
|
|
|
pos int = -1 // custom default value
|
2020-05-16 16:17:27 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
2020-05-16 16:19:48 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
All struct fields are zeroed by default during the creation of the struct.
|
|
|
|
|
Array and map fields are allocated.
|
2020-05-17 17:01:02 +03:00
|
|
|
|
|
|
|
|
|
It's also possible to define custom default values.
|
2020-05-16 16:17:27 +03:00
|
|
|
|
|
2020-11-27 16:37:12 +03:00
|
|
|
|
### Required fields
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
struct Foo {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
n int [required]
|
2020-11-27 16:37:12 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You can mark a struct field with the `[required]` attribute, to tell V that
|
|
|
|
|
that field must be initialized when creating an instance of that struct.
|
|
|
|
|
|
|
|
|
|
This example will not compile, since the field `n` isn't explicitly initialized:
|
|
|
|
|
```v failcompile
|
|
|
|
|
_ = Foo{}
|
|
|
|
|
```
|
2020-05-17 17:11:48 +03:00
|
|
|
|
|
2020-07-08 17:02:35 +03:00
|
|
|
|
<a id='short-struct-initialization-syntax' />
|
2020-05-17 17:11:48 +03:00
|
|
|
|
|
2020-07-23 01:37:37 +03:00
|
|
|
|
### Short struct literal syntax
|
|
|
|
|
|
|
|
|
|
```v
|
2020-12-06 00:54:41 +03:00
|
|
|
|
struct Point {
|
|
|
|
|
x int
|
|
|
|
|
y int
|
2020-11-03 03:04:14 +03:00
|
|
|
|
}
|
2020-07-23 01:37:37 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
mut p := Point{
|
|
|
|
|
x: 10
|
|
|
|
|
y: 20
|
|
|
|
|
}
|
2020-07-23 01:37:37 +03:00
|
|
|
|
// you can omit the struct name when it's already known
|
2020-12-06 00:54:41 +03:00
|
|
|
|
p = {
|
|
|
|
|
x: 30
|
|
|
|
|
y: 4
|
|
|
|
|
}
|
2020-07-23 01:37:37 +03:00
|
|
|
|
assert p.y == 4
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Omitting the struct name also works for returning a struct literal or passing one
|
|
|
|
|
as a function argument.
|
|
|
|
|
|
|
|
|
|
#### Trailing struct literal arguments
|
|
|
|
|
|
|
|
|
|
V doesn't have default function arguments or named arguments, for that trailing struct
|
|
|
|
|
literal syntax can be used instead:
|
2020-05-17 17:11:48 +03:00
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
struct ButtonConfig {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
text string
|
|
|
|
|
is_disabled bool
|
|
|
|
|
width int = 70
|
|
|
|
|
height int = 20
|
2020-05-17 17:11:48 +03:00
|
|
|
|
}
|
2020-05-17 17:15:02 +03:00
|
|
|
|
|
2020-12-01 18:09:33 +03:00
|
|
|
|
struct Button {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
text string
|
|
|
|
|
width int
|
|
|
|
|
height int
|
2020-11-03 03:04:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-17 17:11:48 +03:00
|
|
|
|
fn new_button(c ButtonConfig) &Button {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
return &Button{
|
|
|
|
|
width: c.width
|
|
|
|
|
height: c.height
|
|
|
|
|
text: c.text
|
|
|
|
|
}
|
2020-05-17 17:11:48 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
button := new_button(text: 'Click me', width: 100)
|
2020-07-08 17:02:35 +03:00
|
|
|
|
// the height is unset, so it's the default value
|
|
|
|
|
assert button.height == 20
|
2020-05-17 17:11:48 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-07-23 01:37:37 +03:00
|
|
|
|
As you can see, both the struct name and braces can be omitted, instead of:
|
2020-05-17 17:15:02 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v ignore
|
2020-05-17 17:15:02 +03:00
|
|
|
|
new_button(ButtonConfig{text:'Click me', width:100})
|
|
|
|
|
```
|
|
|
|
|
|
2020-07-23 01:37:37 +03:00
|
|
|
|
This only works for functions that take a struct for the last argument.
|
2020-05-17 17:15:02 +03:00
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
### Access modifiers
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
Struct fields are private and immutable by default (making structs immutable as well).
|
|
|
|
|
Their access modifiers can be changed with
|
2020-04-21 06:11:50 +03:00
|
|
|
|
`pub` and `mut`. In total, there are 5 possible options:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2019-11-21 04:36:55 +03:00
|
|
|
|
struct Foo {
|
|
|
|
|
a int // private immutable (default)
|
2020-04-23 20:05:27 +03:00
|
|
|
|
mut:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
b int // private mutable
|
|
|
|
|
c int // (you can list multiple fields with the same access modifier)
|
|
|
|
|
pub:
|
2020-06-28 03:19:52 +03:00
|
|
|
|
d int // public immutable (readonly)
|
2020-04-23 20:05:27 +03:00
|
|
|
|
pub mut:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
e int // public, but mutable only in parent module
|
|
|
|
|
__global:
|
|
|
|
|
f int // public and mutable both inside and outside parent module
|
|
|
|
|
} // (not recommended to use, that's why the 'global' keyword
|
|
|
|
|
// starts with __)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For example, here's the `string` type defined in the `builtin` module:
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v ignore
|
2019-11-21 04:36:55 +03:00
|
|
|
|
struct string {
|
|
|
|
|
str byteptr
|
|
|
|
|
pub:
|
|
|
|
|
len int
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It's easy to see from this definition that `string` is an immutable type.
|
|
|
|
|
The byte pointer with the string data is not accessible outside `builtin` at all.
|
2020-05-07 13:30:41 +03:00
|
|
|
|
The `len` field is public, but immutable:
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v failcompile
|
2019-11-21 04:36:55 +03:00
|
|
|
|
fn main() {
|
|
|
|
|
str := 'hello'
|
|
|
|
|
len := str.len // OK
|
|
|
|
|
str.len++ // Compilation error
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
This means that defining public readonly fields is very easy in V,
|
|
|
|
|
no need in getters/setters or properties.
|
2020-05-16 16:36:19 +03:00
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
### Methods
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2019-11-21 04:36:55 +03:00
|
|
|
|
struct User {
|
|
|
|
|
age int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (u User) can_register() bool {
|
|
|
|
|
return u.age > 16
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
user := User{age: 10}
|
|
|
|
|
println(user.can_register()) // "false"
|
|
|
|
|
|
|
|
|
|
user2 := User{age: 20}
|
|
|
|
|
println(user2.can_register()) // "true"
|
|
|
|
|
```
|
|
|
|
|
|
2020-07-23 19:55:54 +03:00
|
|
|
|
V doesn't have classes, but you can define methods on types.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
A method is a function with a special receiver argument.
|
|
|
|
|
The receiver appears in its own argument list between the `fn` keyword and the method name.
|
|
|
|
|
|
|
|
|
|
In this example, the `can_register` method has a receiver of type `User` named `u`.
|
|
|
|
|
The convention is not to use receiver names like `self` or `this`,
|
|
|
|
|
but a short, preferably one letter long, name.
|
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
## Functions 2
|
|
|
|
|
|
|
|
|
|
### Pure functions by default
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
V functions are pure by default, meaning that their return values are a function of their
|
|
|
|
|
arguments only, and their evaluation has no side effects (besides I/O).
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
This is achieved by a lack of global variables and all function arguments being
|
|
|
|
|
immutable by default, even when [references](#references) are passed.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
V is not a purely functional language however.
|
|
|
|
|
|
2020-06-30 15:29:55 +03:00
|
|
|
|
There is a compiler flag to enable global variables (`--enable-globals`), but this is
|
|
|
|
|
intended for low-level applications like kernels and drivers.
|
|
|
|
|
|
|
|
|
|
### Mutable arguments
|
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
It is possible to modify function arguments by using the keyword `mut`:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2019-11-21 04:36:55 +03:00
|
|
|
|
struct User {
|
2020-11-03 03:04:14 +03:00
|
|
|
|
name string
|
2020-04-23 20:05:27 +03:00
|
|
|
|
mut:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
is_registered bool
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-21 06:11:50 +03:00
|
|
|
|
fn (mut u User) register() {
|
2019-11-21 04:36:55 +03:00
|
|
|
|
u.is_registered = true
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-21 06:11:50 +03:00
|
|
|
|
mut user := User{}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
println(user.is_registered) // "false"
|
|
|
|
|
user.register()
|
|
|
|
|
println(user.is_registered) // "true"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
In this example, the receiver (which is simply the first argument) is marked as mutable,
|
|
|
|
|
so `register()` can change the user object. The same works with non-receiver arguments:
|
|
|
|
|
|
|
|
|
|
```v
|
2020-04-21 06:11:50 +03:00
|
|
|
|
fn multiply_by_2(mut arr []int) {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
for i in 0 .. arr.len {
|
|
|
|
|
arr[i] *= 2
|
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-21 06:11:50 +03:00
|
|
|
|
mut nums := [1, 2, 3]
|
|
|
|
|
multiply_by_2(mut nums)
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println(nums)
|
|
|
|
|
// "[2, 4, 6]"
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-04-21 06:11:50 +03:00
|
|
|
|
Note, that you have to add `mut` before `nums` when calling this function. This makes
|
2019-11-21 04:36:55 +03:00
|
|
|
|
it clear that the function being called will modify the value.
|
|
|
|
|
|
|
|
|
|
It is preferable to return values instead of modifying arguments.
|
|
|
|
|
Modifying arguments should only be done in performance-critical parts of your application
|
|
|
|
|
to reduce allocations and copying.
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
For this reason V doesn't allow the modification of arguments with primitive types (e.g. integers).
|
|
|
|
|
Only more complex types such as arrays and maps may be modified.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
Use `user.register()` or `user = register(user)`
|
2020-04-21 06:11:50 +03:00
|
|
|
|
instead of `register(mut user)`.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
V makes it easy to return a modified version of an object:
|
|
|
|
|
|
|
|
|
|
```v
|
2020-12-06 00:54:41 +03:00
|
|
|
|
struct User {
|
|
|
|
|
name string
|
|
|
|
|
age int
|
|
|
|
|
is_registered bool
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
fn register(u User) User {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
return {
|
|
|
|
|
u |
|
|
|
|
|
is_registered: true
|
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
mut user := User{
|
|
|
|
|
name: 'abc'
|
|
|
|
|
age: 23
|
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
user = register(user)
|
2020-11-03 03:04:14 +03:00
|
|
|
|
println(user)
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
### Anonymous & high order functions
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
fn sqr(n int) int {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
return n * n
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
fn run(value int, op fn (int) int) int {
|
|
|
|
|
return op(value)
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
fn main() {
|
|
|
|
|
println(run(5, sqr)) // "25"
|
|
|
|
|
// Anonymous functions can be declared inside other functions:
|
|
|
|
|
double_fn := fn (n int) int {
|
|
|
|
|
return n + n
|
|
|
|
|
}
|
|
|
|
|
println(run(5, double_fn)) // "10"
|
|
|
|
|
// Functions can be passed around without assigning them to variables:
|
|
|
|
|
res := run(5, fn (n int) int {
|
|
|
|
|
return n + n
|
|
|
|
|
})
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## References
|
|
|
|
|
|
|
|
|
|
```v
|
2020-12-23 01:43:48 +03:00
|
|
|
|
struct Foo {}
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
fn (foo Foo) bar_method() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
// ...
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn bar_function(foo Foo) {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
// ...
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
If a function argument is immutable (like `foo` in the examples above)
|
2020-10-25 15:55:57 +03:00
|
|
|
|
V can pass it either by value or by reference. The compiler will decide,
|
2020-05-07 13:30:41 +03:00
|
|
|
|
and the developer doesn't need to think about it.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
You no longer need to remember whether you should pass the struct by value
|
|
|
|
|
or by reference.
|
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
You can ensure that the struct is always passed by reference by
|
2019-11-21 04:36:55 +03:00
|
|
|
|
adding `&`:
|
|
|
|
|
|
|
|
|
|
```v
|
2020-12-06 00:54:41 +03:00
|
|
|
|
struct Foo {
|
|
|
|
|
abc int
|
|
|
|
|
}
|
2020-11-03 03:04:14 +03:00
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
fn (foo &Foo) bar() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println(foo.abc)
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`foo` is still immutable and can't be changed. For that,
|
2020-10-25 15:55:57 +03:00
|
|
|
|
`(mut foo Foo)` must be used.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
In general, V's references are similar to Go pointers and C++ references.
|
2020-11-03 03:04:14 +03:00
|
|
|
|
For example, a generic tree structure definition would look like this:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v wip
|
2019-11-21 04:36:55 +03:00
|
|
|
|
struct Node<T> {
|
|
|
|
|
val T
|
|
|
|
|
left &Node
|
|
|
|
|
right &Node
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Constants
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
const (
|
2020-12-06 00:54:41 +03:00
|
|
|
|
pi = 3.14
|
|
|
|
|
world = '世界'
|
2019-11-21 04:36:55 +03:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
println(pi)
|
|
|
|
|
println(world)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Constants are declared with `const`. They can only be defined
|
|
|
|
|
at the module level (outside of functions).
|
|
|
|
|
|
|
|
|
|
Constant values can never be changed.
|
|
|
|
|
|
|
|
|
|
V constants are more flexible than in most languages. You can assign more complex values:
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
struct Color {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
r int
|
|
|
|
|
g int
|
|
|
|
|
b int
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
fn rgb(r int, g int, b int) Color {
|
|
|
|
|
return Color{
|
|
|
|
|
r: r
|
|
|
|
|
g: g
|
|
|
|
|
b: b
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
const (
|
2020-12-06 00:54:41 +03:00
|
|
|
|
numbers = [1, 2, 3]
|
|
|
|
|
red = Color{
|
|
|
|
|
r: 255
|
|
|
|
|
g: 0
|
|
|
|
|
b: 0
|
|
|
|
|
}
|
|
|
|
|
// evaluate function call at compile-time
|
|
|
|
|
blue = rgb(0, 0, 255)
|
2019-11-21 04:36:55 +03:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
println(numbers)
|
|
|
|
|
println(red)
|
|
|
|
|
println(blue)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Global variables are not allowed, so this can be really useful.
|
|
|
|
|
|
2020-05-21 17:34:57 +03:00
|
|
|
|
<!--
|
2019-11-21 04:36:55 +03:00
|
|
|
|
When naming constants, snake_case must be used.
|
|
|
|
|
Many people prefer all caps consts: `TOP_CITIES`. This wouldn't work
|
|
|
|
|
well in V, because consts are a lot more powerful than in other languages.
|
|
|
|
|
They can represent complex structures, and this is used quite often since there
|
|
|
|
|
are no globals:
|
2020-05-21 17:34:57 +03:00
|
|
|
|
-->
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v ignore
|
2019-11-21 04:36:55 +03:00
|
|
|
|
println('Top cities: $top_cities.filter(.usa)')
|
|
|
|
|
```
|
|
|
|
|
|
2020-12-26 23:13:07 +03:00
|
|
|
|
## Builtin functions
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-26 23:13:07 +03:00
|
|
|
|
Some functions are builtin like `println`. Here is the complete list:
|
|
|
|
|
|
|
|
|
|
```v ignore
|
|
|
|
|
fn print(s string) // print anything on sdtout
|
|
|
|
|
fn println(s string) // print anything and a newline on sdtout
|
|
|
|
|
|
|
|
|
|
fn eprint(s string) // same as print(), but use stderr
|
|
|
|
|
fn eprintln(s string) // same as println(), but use stderr
|
|
|
|
|
|
|
|
|
|
fn exit(code int) // terminate the program with a custom error code
|
|
|
|
|
fn panic(s string) // print a message and backtraces on stderr, and terminate the program with error code 1
|
|
|
|
|
fn print_backtrace() // print backtraces on stderr
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`println` is a simple yet powerful builtin function, that can print anything:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
strings, numbers, arrays, maps, structs.
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-11-03 03:04:14 +03:00
|
|
|
|
struct User{ name string age int }
|
2019-11-21 04:36:55 +03:00
|
|
|
|
println(1) // "1"
|
|
|
|
|
println('hi') // "hi"
|
|
|
|
|
println([1,2,3]) // "[1, 2, 3]"
|
|
|
|
|
println(User{name:'Bob', age:20}) // "User{name:'Bob', age:20}"
|
|
|
|
|
```
|
|
|
|
|
|
2020-12-28 21:29:58 +03:00
|
|
|
|
## Custom print of types
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
If you want to define a custom print value for your type, simply define a
|
2020-06-30 15:29:55 +03:00
|
|
|
|
`.str() string` method:
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
struct Color {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
r int
|
|
|
|
|
g int
|
|
|
|
|
b int
|
2020-06-30 15:29:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
pub fn (c Color) str() string {
|
|
|
|
|
return '{$c.r, $c.g, $c.b}'
|
|
|
|
|
}
|
2020-06-30 15:29:55 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
red := Color{
|
|
|
|
|
r: 255
|
|
|
|
|
g: 0
|
|
|
|
|
b: 0
|
|
|
|
|
}
|
2020-06-30 15:29:55 +03:00
|
|
|
|
println(red)
|
|
|
|
|
```
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
## Modules
|
|
|
|
|
|
2020-10-11 21:36:41 +03:00
|
|
|
|
Every file in the root of a folder is part of the same module.
|
2020-10-25 15:55:57 +03:00
|
|
|
|
Simple programs don't need to specify module name, in which case it defaults to 'main'.
|
2020-10-11 21:36:41 +03:00
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
V is a very modular language. Creating reusable modules is encouraged and is
|
2020-10-25 15:55:57 +03:00
|
|
|
|
quite easy to do.
|
2020-09-25 21:34:58 +03:00
|
|
|
|
To create a new module, create a directory with your module's name containing
|
2019-11-21 04:36:55 +03:00
|
|
|
|
.v files with code:
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```shell
|
2019-11-21 04:36:55 +03:00
|
|
|
|
cd ~/code/modules
|
|
|
|
|
mkdir mymodule
|
2020-09-21 17:31:11 +03:00
|
|
|
|
vim mymodule/myfile.v
|
2020-09-25 21:34:58 +03:00
|
|
|
|
```
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v failcompile
|
2020-09-21 17:31:11 +03:00
|
|
|
|
// myfile.v
|
2019-11-21 04:36:55 +03:00
|
|
|
|
module mymodule
|
|
|
|
|
|
|
|
|
|
// To export a function we have to use `pub`
|
|
|
|
|
pub fn say_hi() {
|
|
|
|
|
println('hello from mymodule!')
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-09-25 21:34:58 +03:00
|
|
|
|
You can now use `mymodule` in your code:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v failcompile
|
2019-11-21 04:36:55 +03:00
|
|
|
|
import mymodule
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
mymodule.say_hi()
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-09-25 21:34:58 +03:00
|
|
|
|
* Module names should be short, under 10 characters.
|
2020-12-27 13:23:43 +03:00
|
|
|
|
* Module names must use `snake_case`.
|
2020-09-25 21:34:58 +03:00
|
|
|
|
* Circular imports are not allowed.
|
|
|
|
|
* You can have as many .v files in a module as you want.
|
|
|
|
|
* You can create modules anywhere.
|
|
|
|
|
* All modules are compiled statically into a single executable.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-09-25 21:34:58 +03:00
|
|
|
|
### `init` functions
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-15 13:32:28 +03:00
|
|
|
|
If you want a module to automatically call some setup/initialization code when it is imported,
|
2020-09-25 21:34:58 +03:00
|
|
|
|
you can use a module `init` function:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
```v
|
2020-07-11 22:20:28 +03:00
|
|
|
|
fn init() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
// your setup code here ...
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-10-15 13:32:28 +03:00
|
|
|
|
The `init` function cannot be public - it will be called automatically. This feature is
|
2020-09-25 21:34:58 +03:00
|
|
|
|
particularly useful for initializing a C library.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
## Types 2
|
|
|
|
|
|
|
|
|
|
### Interfaces
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
```v
|
2020-09-30 08:42:23 +03:00
|
|
|
|
struct Dog {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
breed string
|
2020-09-30 08:42:23 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-23 01:43:48 +03:00
|
|
|
|
struct Cat {}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
fn (d Dog) speak() string {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
return 'woof'
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (c Cat) speak() string {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
return 'meow'
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface Speaker {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
speak() string
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-30 08:42:23 +03:00
|
|
|
|
dog := Dog{'Leonberger'}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
cat := Cat{}
|
2020-11-12 22:10:09 +03:00
|
|
|
|
mut arr := []Speaker{}
|
|
|
|
|
arr << dog
|
|
|
|
|
arr << cat
|
|
|
|
|
for item in arr {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
item.speak()
|
2020-11-12 22:10:09 +03:00
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
A type implements an interface by implementing its methods.
|
|
|
|
|
There is no explicit declaration of intent, no "implements" keyword.
|
|
|
|
|
|
2020-11-12 22:10:09 +03:00
|
|
|
|
We can test the underlying type of an interface using dynamic cast operators:
|
|
|
|
|
```v oksyntax
|
|
|
|
|
fn announce(s Speaker) {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
if s is Dog {
|
|
|
|
|
println('a $s.breed') // `s` is automatically cast to `Dog` (smart cast)
|
|
|
|
|
} else if s is Cat {
|
|
|
|
|
println('a cat')
|
|
|
|
|
} else {
|
|
|
|
|
println('something else')
|
|
|
|
|
}
|
2020-11-12 22:10:09 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
For more information, see [Dynamic casts](#dynamic-casts).
|
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
### Enums
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2019-11-21 04:36:55 +03:00
|
|
|
|
enum Color {
|
|
|
|
|
red green blue
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-21 06:11:50 +03:00
|
|
|
|
mut color := Color.red
|
2019-11-21 04:36:55 +03:00
|
|
|
|
// V knows that `color` is a `Color`. No need to use `color = Color.green` here.
|
|
|
|
|
color = .green
|
2020-08-24 12:14:35 +03:00
|
|
|
|
println(color) // "green"
|
|
|
|
|
|
|
|
|
|
match color {
|
2020-12-04 21:34:05 +03:00
|
|
|
|
.red { println('the color was red') }
|
|
|
|
|
.green { println('the color was green') }
|
|
|
|
|
.blue { println('the color was blue') }
|
2020-08-24 12:14:35 +03:00
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
Enum match must be exhaustive or have an `else` branch.
|
|
|
|
|
This ensures that if a new enum field is added, it's handled everywhere in the code.
|
2020-08-24 12:14:35 +03:00
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
### Sum types
|
2020-05-05 16:42:53 +03:00
|
|
|
|
|
2020-07-06 19:09:38 +03:00
|
|
|
|
A sum type instance can hold a value of several different types. Use the `type`
|
|
|
|
|
keyword to declare a sum type:
|
2020-05-05 16:42:53 +03:00
|
|
|
|
|
2020-07-06 19:09:38 +03:00
|
|
|
|
```v
|
2020-12-23 01:43:48 +03:00
|
|
|
|
struct Moon {}
|
2020-05-05 16:42:53 +03:00
|
|
|
|
|
2020-12-23 01:43:48 +03:00
|
|
|
|
struct Mars {}
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2020-12-23 01:43:48 +03:00
|
|
|
|
struct Venus {}
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
|
|
|
|
type World = Mars | Moon | Venus
|
2020-05-05 16:42:53 +03:00
|
|
|
|
|
2020-07-06 19:09:38 +03:00
|
|
|
|
sum := World(Moon{})
|
2020-11-14 14:59:03 +03:00
|
|
|
|
assert sum.type_name() == 'Moon'
|
2020-11-03 03:04:14 +03:00
|
|
|
|
println(sum)
|
2020-05-05 16:42:53 +03:00
|
|
|
|
```
|
2020-12-01 18:09:33 +03:00
|
|
|
|
The built-in method `type_name` returns the name of the currently held
|
2020-11-14 14:59:03 +03:00
|
|
|
|
type.
|
2020-05-05 16:42:53 +03:00
|
|
|
|
|
2020-11-12 22:10:09 +03:00
|
|
|
|
#### Dynamic casts
|
|
|
|
|
|
2020-07-06 19:09:38 +03:00
|
|
|
|
To check whether a sum type instance holds a certain type, use `sum is Type`.
|
2020-07-14 20:24:59 +03:00
|
|
|
|
To cast a sum type to one of its variants you can use `sum as Type`:
|
2020-05-05 16:42:53 +03:00
|
|
|
|
|
|
|
|
|
```v
|
2020-12-23 01:43:48 +03:00
|
|
|
|
struct Moon {}
|
2020-11-12 22:13:37 +03:00
|
|
|
|
|
2020-12-23 01:43:48 +03:00
|
|
|
|
struct Mars {}
|
2020-07-06 19:09:38 +03:00
|
|
|
|
|
2020-12-23 01:43:48 +03:00
|
|
|
|
struct Venus {}
|
2020-11-12 22:13:37 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
type World = Mars | Moon | Venus
|
2020-07-06 19:09:38 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
fn (m Mars) dust_storm() bool {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
mut w := World(Moon{})
|
|
|
|
|
assert w is Moon
|
|
|
|
|
w = Mars{}
|
|
|
|
|
// use `as` to access the Mars instance
|
|
|
|
|
mars := w as Mars
|
|
|
|
|
if mars.dust_storm() {
|
|
|
|
|
println('bad weather!')
|
|
|
|
|
}
|
2020-07-06 19:09:38 +03:00
|
|
|
|
}
|
2020-05-05 16:42:53 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-11-12 22:13:37 +03:00
|
|
|
|
`as` will panic if `w` doesn't hold a `Mars` instance.
|
|
|
|
|
A safer way is to use a smart cast.
|
|
|
|
|
|
|
|
|
|
#### Smart casting
|
|
|
|
|
|
|
|
|
|
```v oksyntax
|
|
|
|
|
if w is Mars {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
assert typeof(w).name == 'Mars'
|
|
|
|
|
if w.dust_storm() {
|
|
|
|
|
println('bad weather!')
|
|
|
|
|
}
|
2020-11-12 22:13:37 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
2020-12-01 18:09:33 +03:00
|
|
|
|
`w` has type `Mars` inside the body of the `if` statement. This is
|
2020-11-12 22:13:37 +03:00
|
|
|
|
known as *flow-sensitive typing*. You can also specify a variable name:
|
|
|
|
|
|
2020-11-27 16:28:40 +03:00
|
|
|
|
```v ignore
|
2020-11-12 22:13:37 +03:00
|
|
|
|
if w is Mars as mars {
|
|
|
|
|
assert typeof(w).name == 'World'
|
|
|
|
|
if mars.dust_storm() {
|
|
|
|
|
println('bad weather!')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
`w` keeps its original type. This form is necessary if `w` is a more
|
|
|
|
|
complex expression than just a variable name.
|
|
|
|
|
|
|
|
|
|
#### Matching sum types
|
2020-07-14 20:24:59 +03:00
|
|
|
|
|
2020-07-06 19:09:38 +03:00
|
|
|
|
You can also use `match` to determine the variant:
|
2020-06-18 18:37:41 +03:00
|
|
|
|
|
|
|
|
|
```v
|
2020-12-23 01:43:48 +03:00
|
|
|
|
struct Moon {}
|
2020-11-12 22:13:37 +03:00
|
|
|
|
|
2020-12-23 01:43:48 +03:00
|
|
|
|
struct Mars {}
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2020-12-23 01:43:48 +03:00
|
|
|
|
struct Venus {}
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
|
|
|
|
type World = Mars | Moon | Venus
|
2020-11-12 22:13:37 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
fn open_parachutes(n int) {
|
|
|
|
|
println(n)
|
|
|
|
|
}
|
2020-11-12 22:13:37 +03:00
|
|
|
|
|
2020-07-06 19:09:38 +03:00
|
|
|
|
fn land(w World) {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
match w {
|
|
|
|
|
Moon {} // no atmosphere
|
|
|
|
|
Mars {
|
|
|
|
|
// light atmosphere
|
|
|
|
|
open_parachutes(3)
|
|
|
|
|
}
|
|
|
|
|
Venus {
|
|
|
|
|
// heavy atmosphere
|
|
|
|
|
open_parachutes(1)
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-06 19:09:38 +03:00
|
|
|
|
}
|
2020-06-18 18:37:41 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-07-06 19:09:38 +03:00
|
|
|
|
`match` must have a pattern for each variant or have an `else` branch.
|
|
|
|
|
|
2020-10-25 15:55:57 +03:00
|
|
|
|
There are two ways to access the cast variant inside a match branch:
|
2020-06-18 18:37:41 +03:00
|
|
|
|
- the shadowed match variable
|
|
|
|
|
- using `as` to specify a variable name
|
|
|
|
|
|
2020-11-27 16:28:40 +03:00
|
|
|
|
```v ignore
|
2020-11-03 03:04:14 +03:00
|
|
|
|
struct Moon {}
|
|
|
|
|
struct Mars {}
|
|
|
|
|
struct Venus {}
|
|
|
|
|
|
|
|
|
|
type World = Moon | Mars | Venus
|
|
|
|
|
|
|
|
|
|
fn (m Moon) moon_walk() {}
|
|
|
|
|
fn (m Mars) shiver() {}
|
|
|
|
|
fn (v Venus) sweat() {}
|
2020-07-06 19:09:38 +03:00
|
|
|
|
|
|
|
|
|
fn pass_time(w World) {
|
|
|
|
|
match w {
|
2020-08-24 12:14:35 +03:00
|
|
|
|
// using the shadowed match variable, in this case `w` (smart cast)
|
2020-07-06 19:09:38 +03:00
|
|
|
|
Moon { w.moon_walk() }
|
|
|
|
|
Mars { w.shiver() }
|
|
|
|
|
else {}
|
|
|
|
|
}
|
2020-07-08 16:56:49 +03:00
|
|
|
|
// using `as` to specify a name for each value
|
|
|
|
|
match w as var {
|
|
|
|
|
Mars { var.shiver() }
|
|
|
|
|
Venus { var.sweat() }
|
2020-07-06 19:09:38 +03:00
|
|
|
|
else {
|
|
|
|
|
// w is of type World
|
2020-07-08 16:56:49 +03:00
|
|
|
|
assert w is Moon
|
2020-07-06 19:09:38 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-18 18:37:41 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-09-18 02:15:25 +03:00
|
|
|
|
Note: shadowing only works when the match expression is a variable.
|
|
|
|
|
It will not work on struct fields, array indexes, or map keys.
|
2020-06-18 18:52:21 +03:00
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
### Option/Result types and error handling
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-07-14 20:24:59 +03:00
|
|
|
|
Option types are declared with `?Type`:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```v
|
|
|
|
|
struct User {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
id int
|
|
|
|
|
name string
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Repo {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
users []User
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (r Repo) find_user_by_id(id int) ?User {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
for user in r.users {
|
|
|
|
|
if user.id == id {
|
|
|
|
|
// V automatically wraps this into an option type
|
|
|
|
|
return user
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return error('User $id not found')
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
repo := Repo{
|
|
|
|
|
users: [User{1, 'Andrew'}, User{2, 'Bob'},
|
|
|
|
|
User{10, 'Charles'},
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
user := repo.find_user_by_id(10) or { // Option types must be handled by `or` blocks
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
println(user.id) // "10"
|
|
|
|
|
println(user.name) // "Charles"
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
V combines `Option` and `Result` into one type, so you don't need to decide which one to use.
|
|
|
|
|
|
2020-05-07 19:01:09 +03:00
|
|
|
|
The amount of work required to "upgrade" a function to an optional function is minimal;
|
2019-11-21 04:36:55 +03:00
|
|
|
|
you have to add a `?` to the return type and return an error when something goes wrong.
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
If you don't need to return an error message, you can simply `return none`
|
|
|
|
|
(this is a more efficient equivalent of `return error("")`).
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-05-07 19:01:09 +03:00
|
|
|
|
This is the primary mechanism for error handling in V. They are still values, like in Go,
|
2019-11-21 04:36:55 +03:00
|
|
|
|
but the advantage is that errors can't be unhandled, and handling them is a lot less verbose.
|
2020-07-11 11:26:22 +03:00
|
|
|
|
Unlike other languages, V does not handle exceptions with `throw/try/catch` blocks.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
`err` is defined inside an `or` block and is set to the string message passed
|
|
|
|
|
to the `error()` function. `err` is empty if `none` was returned.
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v oksyntax
|
2019-11-21 04:36:55 +03:00
|
|
|
|
user := repo.find_user_by_id(7) or {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println(err) // "User 7 not found"
|
|
|
|
|
return
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-07-14 20:24:59 +03:00
|
|
|
|
### Handling optionals
|
2020-07-12 20:27:41 +03:00
|
|
|
|
|
2020-07-15 22:57:41 +03:00
|
|
|
|
There are four ways of handling an optional. The first method is to
|
2020-07-14 20:24:59 +03:00
|
|
|
|
propagate the error:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
```v
|
2020-07-14 20:24:59 +03:00
|
|
|
|
import net.http
|
|
|
|
|
|
|
|
|
|
fn f(url string) ?string {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
resp := http.get(url) ?
|
|
|
|
|
return resp.text
|
2020-07-14 20:24:59 +03:00
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-08-12 20:14:13 +03:00
|
|
|
|
`http.get` returns `?http.Response`. Because `?` follows the call, the
|
|
|
|
|
error will be propagated to the caller of `f`. When using `?` after a
|
|
|
|
|
function call producing an optional, the enclosing function must return
|
|
|
|
|
an optional as well. If error propagation is used in the `main()`
|
|
|
|
|
function it will `panic` instead, since the error cannot be propagated
|
2020-07-14 20:24:59 +03:00
|
|
|
|
any further.
|
2020-05-07 19:01:09 +03:00
|
|
|
|
|
2020-07-14 20:24:59 +03:00
|
|
|
|
The body of `f` is essentially a condensed version of:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v ignore
|
2020-12-06 00:54:41 +03:00
|
|
|
|
resp := http.get(url) or { return error(err) }
|
2020-07-14 20:24:59 +03:00
|
|
|
|
return resp.text
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-07-14 20:24:59 +03:00
|
|
|
|
---
|
|
|
|
|
The second method is to break from execution early:
|
2020-07-12 20:27:41 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v oksyntax
|
2020-12-06 00:54:41 +03:00
|
|
|
|
user := repo.find_user_by_id(7) or { return }
|
2020-07-12 20:27:41 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
Here, you can either call `panic()` or `exit()`, which will stop the execution of the
|
|
|
|
|
entire program, or use a control flow statement (`return`, `break`, `continue`, etc)
|
|
|
|
|
to break from the current block.
|
2020-07-12 20:27:41 +03:00
|
|
|
|
Note that `break` and `continue` can only be used inside a `for` loop.
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
V does not have a way to forcibly "unwrap" an optional (as other languages do,
|
|
|
|
|
for instance Rust's `unwrap()` or Swift's `!`). To do this, use `or { panic(err) }` instead.
|
2020-07-15 22:57:41 +03:00
|
|
|
|
|
2020-07-14 20:24:59 +03:00
|
|
|
|
---
|
2020-10-20 21:14:56 +03:00
|
|
|
|
The third method is to provide a default value at the end of the `or` block.
|
|
|
|
|
In case of an error, that value would be assigned instead,
|
|
|
|
|
so it must have the same type as the content of the `Option` being handled.
|
2020-07-12 20:27:41 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-07-12 20:27:41 +03:00
|
|
|
|
fn do_something(s string) ?string {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
if s == 'foo' {
|
|
|
|
|
return 'foo'
|
|
|
|
|
}
|
|
|
|
|
return error('invalid string') // Could be `return none` as well
|
2020-07-12 20:27:41 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a := do_something('foo') or { 'default' } // a will be 'foo'
|
|
|
|
|
b := do_something('bar') or { 'default' } // b will be 'default'
|
2020-11-03 03:04:14 +03:00
|
|
|
|
println(a)
|
|
|
|
|
println(b)
|
2020-07-12 20:27:41 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-07-15 22:57:41 +03:00
|
|
|
|
---
|
|
|
|
|
The fourth method is to use `if` unwrapping:
|
|
|
|
|
|
|
|
|
|
```v
|
2020-11-03 03:04:14 +03:00
|
|
|
|
import net.http
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
if resp := http.get('https://google.com') {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println(resp.text) // resp is a http.Response, not an optional
|
2020-07-15 22:57:41 +03:00
|
|
|
|
} else {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println(err)
|
2020-07-15 22:57:41 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
2020-08-12 20:14:13 +03:00
|
|
|
|
Above, `http.get` returns a `?http.Response`. `resp` is only in scope for the first
|
2020-07-15 22:57:41 +03:00
|
|
|
|
`if` branch. `err` is only in scope for the `else` branch.
|
2020-04-23 06:35:33 +03:00
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
## Generics
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v wip
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
struct Repo<T> {
|
|
|
|
|
db DB
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn new_repo<T>(db DB) Repo<T> {
|
|
|
|
|
return Repo<T>{db: db}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This is a generic function. V will generate it for every type it's used with.
|
|
|
|
|
fn (r Repo<T>) find_by_id(id int) ?T {
|
|
|
|
|
table_name := T.name // in this example getting the name of the type gives us the table name
|
|
|
|
|
return r.db.query_one<T>('select * from $table_name where id = ?', id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db := new_db()
|
2020-10-24 20:37:14 +03:00
|
|
|
|
users_repo := new_repo<User>(db) // returns Repo<User>
|
|
|
|
|
posts_repo := new_repo<Post>(db) // returns Repo<Post>
|
|
|
|
|
user := users_repo.find_by_id(1)? // find_by_id<User>
|
|
|
|
|
post := posts_repo.find_by_id(1)? // find_by_id<Post>
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
2020-10-24 20:37:14 +03:00
|
|
|
|
At the moment only one type parameter named `T` is supported.
|
|
|
|
|
|
|
|
|
|
Currently generic function definitions must declare their type parameters, but in
|
|
|
|
|
future V will infer generic type parameters from single-letter type names in
|
2020-12-01 18:09:33 +03:00
|
|
|
|
runtime parameter types. This is why `find_by_id` can omit `<T>`, because the
|
2020-10-24 20:37:14 +03:00
|
|
|
|
receiver argument `r` uses a generic type `T`.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-08-01 16:34:23 +03:00
|
|
|
|
Another example:
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-10-24 20:37:14 +03:00
|
|
|
|
fn compare<T>(a T, b T) int {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
if a < b {
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
if a > b {
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
return 0
|
2020-08-01 16:34:23 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-24 20:37:14 +03:00
|
|
|
|
// compare<int>
|
|
|
|
|
println(compare(1, 0)) // Outputs: 1
|
|
|
|
|
println(compare(1, 1)) // 0
|
|
|
|
|
println(compare(1, 2)) // -1
|
2020-08-01 16:34:23 +03:00
|
|
|
|
|
2020-10-24 20:37:14 +03:00
|
|
|
|
// compare<string>
|
|
|
|
|
println(compare('1', '0')) // Outputs: 1
|
|
|
|
|
println(compare('1', '1')) // 0
|
|
|
|
|
println(compare('1', '2')) // -1
|
2020-08-01 16:34:23 +03:00
|
|
|
|
|
2020-10-24 20:37:14 +03:00
|
|
|
|
// compare<f64>
|
|
|
|
|
println(compare(1.1, 1.0)) // Outputs: 1
|
|
|
|
|
println(compare(1.1, 1.1)) // 0
|
|
|
|
|
println(compare(1.1, 1.2)) // -1
|
2020-08-01 16:34:23 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
## Concurrency
|
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
V's model of concurrency is very similar to Go's. To run `foo()` concurrently, just
|
|
|
|
|
call it with `go foo()`. Right now, it launches the function on a new system
|
|
|
|
|
thread. Soon coroutines and a scheduler will be implemented.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-08-02 00:08:57 +03:00
|
|
|
|
```v
|
|
|
|
|
import sync
|
|
|
|
|
import time
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
fn task(id int, duration int, mut wg sync.WaitGroup) {
|
|
|
|
|
println('task $id begin')
|
|
|
|
|
time.sleep_ms(duration)
|
|
|
|
|
println('task $id end')
|
|
|
|
|
wg.done()
|
2020-08-02 00:08:57 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
mut wg := sync.new_waitgroup()
|
|
|
|
|
wg.add(3)
|
|
|
|
|
go task(1, 500, mut wg)
|
|
|
|
|
go task(2, 900, mut wg)
|
|
|
|
|
go task(3, 100, mut wg)
|
|
|
|
|
wg.wait()
|
|
|
|
|
println('done')
|
2020-08-02 00:08:57 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
// Output:
|
|
|
|
|
// task 1 begin
|
|
|
|
|
// task 2 begin
|
|
|
|
|
// task 3 begin
|
|
|
|
|
// task 3 end
|
|
|
|
|
// task 1 end
|
|
|
|
|
// task 2 end
|
|
|
|
|
// done
|
2020-08-02 00:08:57 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-09-20 17:20:56 +03:00
|
|
|
|
### Channels
|
|
|
|
|
Channels are the preferred way to communicate between coroutines. V's channels work basically like
|
|
|
|
|
those in Go. You can push objects into a channel on one end and pop objects from the other end.
|
|
|
|
|
Channels can be buffered or unbuffered and it is possible to `select` from multiple channels.
|
|
|
|
|
|
|
|
|
|
#### Syntax and Usage
|
2020-10-25 15:55:57 +03:00
|
|
|
|
Channels have the type `chan objtype`. An optional buffer length can specified as the `cap` property
|
2020-09-20 17:20:56 +03:00
|
|
|
|
in the declaration:
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-09-20 17:20:56 +03:00
|
|
|
|
ch := chan int{} // unbuffered - "synchronous"
|
|
|
|
|
ch2 := chan f64{cap: 100} // buffer length 100
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Channels do not have to be declared as `mut`. The buffer length is not part of the type but
|
|
|
|
|
a property of the individual channel object. Channels can be passed to coroutines like normal
|
|
|
|
|
variables:
|
|
|
|
|
|
|
|
|
|
```v
|
2020-12-06 00:54:41 +03:00
|
|
|
|
import sync
|
|
|
|
|
|
2020-09-20 17:20:56 +03:00
|
|
|
|
fn f(ch chan int) {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
// ...
|
2020-09-20 17:20:56 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
ch := chan int{}
|
|
|
|
|
go f(ch)
|
|
|
|
|
// ...
|
2020-09-20 17:20:56 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Objects can be pushed to channels using the arrow operator. The same operator can be used to
|
|
|
|
|
pop objects from the other end:
|
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
|
|
|
|
import sync
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
mut ch := chan int{}
|
|
|
|
|
mut ch2 := chan f64{}
|
2020-09-20 17:20:56 +03:00
|
|
|
|
n := 5
|
|
|
|
|
x := 7.3
|
|
|
|
|
ch <- n // push
|
|
|
|
|
ch2 <- x
|
|
|
|
|
mut y := f64(0.0)
|
|
|
|
|
m := <-ch // pop creating new variable
|
|
|
|
|
y = <-ch2 // pop into existing variable
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
A channel can be closed to indicate that no further objects can be pushed. Any attempt
|
|
|
|
|
to do so will then result in a runtime panic (with the exception of `select` and
|
|
|
|
|
`try_push()` - see below). Attempts to pop will return immediately if the
|
|
|
|
|
associated channel has been closed and the buffer is empty. This situation can be
|
|
|
|
|
handled using an or branch (see [Handling Optionals](#handling-optionals)).
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v wip
|
|
|
|
|
mut ch := chan int{}
|
|
|
|
|
mut ch2 := chan f64{}
|
|
|
|
|
// ...
|
2020-09-20 17:20:56 +03:00
|
|
|
|
ch.close()
|
2020-11-03 03:04:14 +03:00
|
|
|
|
// ...
|
2020-09-20 17:20:56 +03:00
|
|
|
|
m := <-ch or {
|
|
|
|
|
println('channel has been closed')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// propagate error
|
|
|
|
|
y := <-ch2 ?
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### Channel Select
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
The `select` command allows monitoring several channels at the same time
|
|
|
|
|
without noticeable CPU load. It consists of a list of possible transfers and associated branches
|
|
|
|
|
of statements - similar to the [match](#match) command:
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v wip
|
|
|
|
|
import time
|
|
|
|
|
fn main () {
|
|
|
|
|
mut c := chan f64{}
|
|
|
|
|
mut ch := chan f64{}
|
|
|
|
|
mut ch2 := chan f64{}
|
|
|
|
|
mut ch3 := chan f64{}
|
|
|
|
|
mut b := 0.0
|
|
|
|
|
// ...
|
|
|
|
|
select {
|
2020-09-20 17:20:56 +03:00
|
|
|
|
a := <-ch {
|
|
|
|
|
// do something with `a`
|
|
|
|
|
}
|
|
|
|
|
b = <-ch2 {
|
|
|
|
|
// do something with predeclared variable `b`
|
|
|
|
|
}
|
|
|
|
|
ch3 <- c {
|
|
|
|
|
// do something if `c` was sent
|
|
|
|
|
}
|
|
|
|
|
> 500 * time.millisecond {
|
|
|
|
|
// do something if no channel has become ready within 0.5s
|
|
|
|
|
}
|
2020-11-03 03:04:14 +03:00
|
|
|
|
}
|
2020-12-01 18:09:33 +03:00
|
|
|
|
}
|
2020-09-20 17:20:56 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The timeout branch is optional. If it is absent `select` waits for an unlimited amount of time.
|
|
|
|
|
It is also possible to proceed immediately if no channel is ready in the moment `select` is called
|
|
|
|
|
by adding an `else { ... }` branch. `else` and `> timeout` are mutually exclusive.
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
The `select` command can be used as an *expression* of type `bool`
|
|
|
|
|
that becomes `false` if all channels are closed:
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v wip
|
2020-09-20 17:20:56 +03:00
|
|
|
|
if select {
|
|
|
|
|
ch <- a {
|
2020-11-03 03:04:14 +03:00
|
|
|
|
// ...
|
2020-09-20 17:20:56 +03:00
|
|
|
|
}
|
2020-09-21 08:34:04 +03:00
|
|
|
|
} {
|
|
|
|
|
// channel was open
|
2020-09-20 17:20:56 +03:00
|
|
|
|
} else {
|
|
|
|
|
// channel is closed
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### Special Channel Features
|
|
|
|
|
|
|
|
|
|
For special purposes there are some builtin properties and methods:
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
|
|
|
|
import sync
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
struct Abc{x int}
|
|
|
|
|
|
|
|
|
|
a := 2.13
|
|
|
|
|
mut ch := chan f64{}
|
|
|
|
|
|
2020-09-20 17:20:56 +03:00
|
|
|
|
res := ch.try_push(a) // try to perform `ch <- a`
|
2020-11-03 03:04:14 +03:00
|
|
|
|
println(res)
|
2020-09-20 17:20:56 +03:00
|
|
|
|
l := ch.len // number of elements in queue
|
|
|
|
|
c := ch.cap // maximum queue length
|
2020-11-03 03:04:14 +03:00
|
|
|
|
println(l)
|
|
|
|
|
println(c)
|
|
|
|
|
|
|
|
|
|
// mut b := Abc{}
|
|
|
|
|
// mut ch2 := chan f64{}
|
|
|
|
|
// res2 := ch2.try_pop(mut b) // try to perform `b = <-ch2
|
2020-09-20 17:20:56 +03:00
|
|
|
|
```
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
The `try_push/pop()` methods will return immediately with one of the results
|
|
|
|
|
`.success`, `.not_ready` or `.closed` - dependent on whether the object has been transferred or
|
|
|
|
|
the reason why not.
|
|
|
|
|
Usage of these methods and properties in production is not recommended -
|
|
|
|
|
algorithms based on them are often subject to race conditions. Use `select` instead.
|
2020-09-20 17:20:56 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
Data can be exchanged between a coroutine and the calling thread via a shared variable.
|
2020-10-25 15:55:57 +03:00
|
|
|
|
Such variables should be created as references and passed to the coroutine as `mut`.
|
2020-10-20 21:14:56 +03:00
|
|
|
|
The underlying `struct` should also contain a `mutex` to lock concurrent access:
|
2020-06-27 00:55:34 +03:00
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
import sync
|
|
|
|
|
|
|
|
|
|
struct St {
|
|
|
|
|
mut:
|
2020-12-06 00:54:41 +03:00
|
|
|
|
x int // share data
|
2020-06-27 00:55:34 +03:00
|
|
|
|
mtx &sync.Mutex
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (mut b St) g() {
|
2020-07-04 13:44:25 +03:00
|
|
|
|
b.mtx.m_lock()
|
2020-06-27 00:55:34 +03:00
|
|
|
|
// read/modify/write b.x
|
|
|
|
|
b.mtx.unlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn caller() {
|
|
|
|
|
mut a := &St{ // create as reference so it's on the heap
|
|
|
|
|
x: 10
|
|
|
|
|
mtx: sync.new_mutex()
|
|
|
|
|
}
|
|
|
|
|
go a.g()
|
2020-07-04 13:44:25 +03:00
|
|
|
|
a.mtx.m_lock()
|
2020-06-27 00:55:34 +03:00
|
|
|
|
// read/modify/write a.x
|
|
|
|
|
a.mtx.unlock()
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
## Decoding JSON
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
import json
|
|
|
|
|
|
2020-09-26 12:34:26 +03:00
|
|
|
|
struct Foo {
|
|
|
|
|
x int
|
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
struct User {
|
|
|
|
|
name string
|
|
|
|
|
age int
|
|
|
|
|
// Use the `skip` attribute to skip certain fields
|
|
|
|
|
foo Foo [skip]
|
|
|
|
|
// If the field name is different in JSON, it can be specified
|
|
|
|
|
last_name string [json: lastName]
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data := '{ "name": "Frodo", "lastName": "Baggins", "age": 25 }'
|
|
|
|
|
user := json.decode(User, data) or {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
eprintln('Failed to decode json')
|
|
|
|
|
return
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
println(user.name)
|
|
|
|
|
println(user.last_name)
|
|
|
|
|
println(user.age)
|
2020-09-26 12:34:26 +03:00
|
|
|
|
// You can also decode JSON arrays:
|
|
|
|
|
sfoos := '[{"x":123},{"x":456}]'
|
2020-12-06 00:54:41 +03:00
|
|
|
|
foos := json.decode([]Foo, sfoos) ?
|
2020-09-26 12:34:26 +03:00
|
|
|
|
println(foos[0].x)
|
|
|
|
|
println(foos[1].x)
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-05-07 19:01:09 +03:00
|
|
|
|
Because of the ubiquitous nature of JSON, support for it is built directly into V.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
The `json.decode` function takes two arguments:
|
|
|
|
|
the first is the type into which the JSON value should be decoded and
|
|
|
|
|
the second is a string containing the JSON data.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
V generates code for JSON encoding and decoding.
|
|
|
|
|
No runtime reflection is used. This results in much better performance.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
## Testing
|
|
|
|
|
|
2020-09-25 19:25:14 +03:00
|
|
|
|
### Asserts
|
|
|
|
|
|
2020-10-15 13:32:28 +03:00
|
|
|
|
```v
|
2020-12-06 00:54:41 +03:00
|
|
|
|
fn foo(mut v []int) {
|
|
|
|
|
v[0] = 1
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
mut v := [20]
|
2020-09-25 19:25:14 +03:00
|
|
|
|
foo(mut v)
|
2020-11-03 03:04:14 +03:00
|
|
|
|
assert v[0] < 4
|
2020-10-15 13:32:28 +03:00
|
|
|
|
```
|
|
|
|
|
An `assert` statement checks that its expression evaluates to `true`. If an assert fails,
|
2020-09-25 19:25:14 +03:00
|
|
|
|
the program will abort. Asserts should only be used to detect programming errors. When an
|
2020-10-15 13:32:28 +03:00
|
|
|
|
assert fails it is reported to *stderr*, and the values on each side of a comparison operator
|
|
|
|
|
(such as `<`, `==`) will be printed when possible. This is useful to easily find an
|
2020-09-25 19:25:14 +03:00
|
|
|
|
unexpected value. Assert statements can be used in any function.
|
|
|
|
|
|
|
|
|
|
### Test files
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```v
|
|
|
|
|
// hello.v
|
2020-09-29 23:48:56 +03:00
|
|
|
|
module main
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2020-09-29 23:48:56 +03:00
|
|
|
|
fn hello() string {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
return 'Hello world'
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2020-09-29 23:48:56 +03:00
|
|
|
|
fn main() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println(hello())
|
2020-09-29 23:48:56 +03:00
|
|
|
|
}
|
2020-09-25 12:51:39 +03:00
|
|
|
|
```
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v failcompile
|
2020-09-29 23:48:56 +03:00
|
|
|
|
module main
|
2019-11-21 04:36:55 +03:00
|
|
|
|
// hello_test.v
|
|
|
|
|
fn test_hello() {
|
|
|
|
|
assert hello() == 'Hello world'
|
|
|
|
|
}
|
|
|
|
|
```
|
2020-10-15 13:32:28 +03:00
|
|
|
|
To run the test above, use `v hello_test.v`. This will check that the function `hello` is
|
|
|
|
|
producing the correct output. V executes all test functions in the file.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-09-25 19:25:14 +03:00
|
|
|
|
* All test functions have to be inside a test file whose name ends in `_test.v`.
|
|
|
|
|
* Test function names must begin with `test_` to mark them for execution.
|
|
|
|
|
* Normal functions can also be defined in test files, and should be called manually. Other
|
|
|
|
|
symbols can also be defined in test files e.g. types.
|
2020-10-25 15:55:57 +03:00
|
|
|
|
* There are two kinds of tests: external and internal.
|
|
|
|
|
* Internal tests must *declare* their module, just like all other .v
|
|
|
|
|
files from the same module. Internal tests can even call private functions in
|
2020-10-15 13:32:28 +03:00
|
|
|
|
the same module.
|
2020-10-25 15:55:57 +03:00
|
|
|
|
* External tests must *import* the modules which they test. They do not
|
2020-10-15 13:32:28 +03:00
|
|
|
|
have access to the private functions/types of the modules. They can test only
|
2020-09-29 23:48:56 +03:00
|
|
|
|
the external/public API that a module provides.
|
|
|
|
|
|
|
|
|
|
In the example above, `test_hello` is an internal test, that can call
|
|
|
|
|
the private function `hello()` because `hello_test.v` has `module main`,
|
2020-10-15 13:32:28 +03:00
|
|
|
|
just like `hello.v`, i.e. both are part of the same module. Note also that
|
|
|
|
|
since `module main` is a regular module like the others, internal tests can
|
2020-09-29 23:48:56 +03:00
|
|
|
|
be used to test private functions in your main program .v files too.
|
2020-09-25 12:51:39 +03:00
|
|
|
|
|
2020-09-25 19:25:14 +03:00
|
|
|
|
You can also define special test functions in a test file:
|
|
|
|
|
* `testsuite_begin` which will be run *before* all other test functions.
|
|
|
|
|
* `testsuite_end` which will be run *after* all other test functions.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-09-25 19:25:14 +03:00
|
|
|
|
#### Running tests
|
2020-02-08 19:00:35 +03:00
|
|
|
|
|
2020-09-25 19:25:14 +03:00
|
|
|
|
To run test functions in an individual test file, use `v foo_test.v`.
|
2020-02-08 19:00:35 +03:00
|
|
|
|
|
2020-10-15 13:32:28 +03:00
|
|
|
|
To test an entire module, use `v test mymodule`. You can also use `v test .` to test
|
|
|
|
|
everything inside your current folder (and subfolders). You can pass the `-stats`
|
2020-09-25 19:25:14 +03:00
|
|
|
|
option to see more details about the individual tests run.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
## Memory management
|
|
|
|
|
|
2020-12-19 08:34:07 +03:00
|
|
|
|
V avoids doing unnecessary allocations in the first place by using value types,
|
|
|
|
|
string buffers, promoting a simple abstraction-free code style.
|
2020-05-07 13:30:41 +03:00
|
|
|
|
|
2020-12-19 08:34:07 +03:00
|
|
|
|
Most objects (~90-100%) are freed by V's autofree engine: the compiler inserts
|
|
|
|
|
necessary free calls automatically during compilation. Remaining small percentage
|
|
|
|
|
of objects is freed via reference counting.
|
|
|
|
|
|
|
|
|
|
The developer doesn't need to change anything in their code. "It just works", like in
|
|
|
|
|
Python, Go, or Java, except there's no heavy GC tracing everything or expensive RC for
|
|
|
|
|
each object.
|
|
|
|
|
|
|
|
|
|
For developers willing to have more low level control, autofree can be disabled with
|
|
|
|
|
`-noautofree`.
|
|
|
|
|
|
2020-12-19 10:34:13 +03:00
|
|
|
|
Note: right now autofree is hidden behind the -autofree flag. It will be enabled by
|
|
|
|
|
default in V 0.3.
|
2020-12-19 08:34:07 +03:00
|
|
|
|
|
|
|
|
|
For example:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
```v
|
2020-11-03 03:04:14 +03:00
|
|
|
|
import strings
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
fn draw_text(s string, x int, y int) {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
// ...
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn draw_scene() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
// ...
|
|
|
|
|
name1 := 'abc'
|
|
|
|
|
name2 := 'def ghi'
|
|
|
|
|
draw_text('hello $name1', 10, 10)
|
|
|
|
|
draw_text('hello $name2', 100, 10)
|
|
|
|
|
draw_text(strings.repeat(`X`, 10000), 10, 50)
|
|
|
|
|
// ...
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The strings don't escape `draw_text`, so they are cleaned up when
|
|
|
|
|
the function exits.
|
|
|
|
|
|
|
|
|
|
In fact, the first two calls won't result in any allocations at all.
|
|
|
|
|
These two strings are small,
|
|
|
|
|
V will use a preallocated buffer for them.
|
|
|
|
|
|
|
|
|
|
```v
|
2020-12-06 00:54:41 +03:00
|
|
|
|
struct User {
|
|
|
|
|
name string
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
fn test() []int {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
number := 7 // stack variable
|
|
|
|
|
user := User{} // struct allocated on stack
|
|
|
|
|
numbers := [1, 2, 3] // array allocated on heap, will be freed as the function exits
|
|
|
|
|
println(number)
|
|
|
|
|
println(user)
|
|
|
|
|
println(numbers)
|
|
|
|
|
numbers2 := [4, 5, 6] // array that's being returned, won't be freed here
|
|
|
|
|
return numbers2
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## ORM
|
|
|
|
|
|
2020-07-23 01:35:42 +03:00
|
|
|
|
(This is still in an alpha state)
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
V has a built-in ORM (object-relational mapping) which supports SQLite,
|
|
|
|
|
and will soon support MySQL, Postgres, MS SQL, and Oracle.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-05-07 19:01:09 +03:00
|
|
|
|
V's ORM provides a number of benefits:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-07-23 01:35:42 +03:00
|
|
|
|
- One syntax for all SQL dialects. (Migrating between databases becomes much easier.)
|
|
|
|
|
- Queries are constructed using V's syntax. (There's no need to learn another syntax.)
|
|
|
|
|
- Safety. (All queries are automatically sanitised to prevent SQL injection.)
|
|
|
|
|
- Compile time checks. (This prevents typos which can only be caught during runtime.)
|
2020-10-20 21:14:56 +03:00
|
|
|
|
- Readability and simplicity. (You don't need to manually parse the results of a query and
|
|
|
|
|
then manually construct objects from the parsed results.)
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
```v nofmt
|
2020-11-03 03:04:14 +03:00
|
|
|
|
import sqlite
|
2020-05-07 13:30:41 +03:00
|
|
|
|
struct Customer { // struct name has to be the same as the table name (for now)
|
2020-07-23 01:35:42 +03:00
|
|
|
|
id int // a field named `id` of integer type must be the first field
|
2019-11-21 04:36:55 +03:00
|
|
|
|
name string
|
|
|
|
|
nr_orders int
|
|
|
|
|
country string
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
db := sqlite.connect('customers.db')?
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
// select count(*) from Customer
|
2020-06-21 17:09:35 +03:00
|
|
|
|
nr_customers := sql db { select count from Customer }
|
2019-11-21 04:36:55 +03:00
|
|
|
|
println('number of all customers: $nr_customers')
|
|
|
|
|
|
|
|
|
|
// V syntax can be used to build queries
|
|
|
|
|
// db.select returns an array
|
2020-06-21 17:09:35 +03:00
|
|
|
|
uk_customers := sql db { select from Customer where country == 'uk' && nr_orders > 0 }
|
2019-11-21 04:36:55 +03:00
|
|
|
|
println(uk_customers.len)
|
|
|
|
|
for customer in uk_customers {
|
|
|
|
|
println('$customer.id - $customer.name')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// by adding `limit 1` we tell V that there will be only one object
|
2020-06-21 17:09:35 +03:00
|
|
|
|
customer := sql db { select from Customer where id == 1 limit 1 }
|
2019-11-21 04:36:55 +03:00
|
|
|
|
println('$customer.id - $customer.name')
|
|
|
|
|
|
|
|
|
|
// insert a new customer
|
|
|
|
|
new_customer := Customer{name: 'Bob', nr_orders: 10}
|
2020-06-21 17:09:35 +03:00
|
|
|
|
sql db { insert new_customer into Customer }
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-06-21 17:12:16 +03:00
|
|
|
|
For more examples, see <a href='https://github.com/vlang/v/blob/master/vlib/orm/orm_test.v'>vlib/orm/orm_test.v</a>.
|
|
|
|
|
|
2020-05-01 11:04:00 +03:00
|
|
|
|
## Writing Documentation
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
The way it works is very similar to Go. It's very simple: there's no need to
|
2020-10-20 21:14:56 +03:00
|
|
|
|
write documentation separately for your code,
|
|
|
|
|
vdoc will generate it from docstrings in the source code.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
Documentation for each function/type/const must be placed right before the declaration:
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
// clearall clears all bits in the array
|
|
|
|
|
fn clearall() {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The comment must start with the name of the definition.
|
|
|
|
|
|
2020-11-30 20:49:57 +03:00
|
|
|
|
Sometimes one line isn't enough to explain what a function does, in that case comments should
|
|
|
|
|
span to the documented function using single line comments:
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
// copy_all recursively copies all elements of the array by their value,
|
|
|
|
|
// if `dupes` is false all duplicate values are eliminated in the process.
|
|
|
|
|
fn copy_all(dupes bool) {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
By convention it is preferred that comments are written in *present tense*.
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
An overview of the module must be placed in the first comment right after the module's name.
|
|
|
|
|
|
2020-05-01 11:55:48 +03:00
|
|
|
|
To generate documentation use vdoc, for example `v doc net.http`.
|
2020-05-01 11:04:00 +03:00
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
## Tools
|
|
|
|
|
|
2020-07-17 14:03:07 +03:00
|
|
|
|
### v fmt
|
2020-06-26 20:36:40 +03:00
|
|
|
|
|
|
|
|
|
You don't need to worry about formatting your code or setting style guidelines.
|
2020-07-17 14:03:07 +03:00
|
|
|
|
`v fmt` takes care of that:
|
2020-06-26 20:36:40 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```shell
|
2020-06-26 20:36:40 +03:00
|
|
|
|
v fmt file.v
|
|
|
|
|
```
|
|
|
|
|
|
2020-07-17 14:03:07 +03:00
|
|
|
|
It's recommended to set up your editor, so that `v fmt -w` runs on every save.
|
2020-06-26 20:36:40 +03:00
|
|
|
|
A vfmt run is usually pretty cheap (takes <30ms).
|
|
|
|
|
|
|
|
|
|
Always run `v fmt -w file.v` before pushing your code.
|
|
|
|
|
|
|
|
|
|
### Profiling
|
2020-05-01 11:04:00 +03:00
|
|
|
|
|
|
|
|
|
V has good support for profiling your programs: `v -profile profile.txt run file.v`
|
|
|
|
|
That will produce a profile.txt file, which you can then analyze.
|
|
|
|
|
|
|
|
|
|
The generated profile.txt file will have lines with 4 columns:
|
|
|
|
|
a) how many times a function was called
|
|
|
|
|
b) how much time in total a function took (in ms)
|
|
|
|
|
c) how much time on average, a call to a function took (in ns)
|
|
|
|
|
d) the name of the v function
|
|
|
|
|
|
|
|
|
|
You can sort on column 3 (average time per function) using:
|
|
|
|
|
`sort -n -k3 profile.txt|tail`
|
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
You can also use stopwatches to measure just portions of your code explicitly:
|
2020-05-01 11:04:00 +03:00
|
|
|
|
```v
|
|
|
|
|
import time
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
sw := time.new_stopwatch({})
|
|
|
|
|
println('Hello world')
|
|
|
|
|
println('Greeting the world took: ${sw.elapsed().nanoseconds()}ns')
|
2020-05-01 11:04:00 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-02-09 11:32:36 +03:00
|
|
|
|
# Advanced Topics
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-07-23 01:36:23 +03:00
|
|
|
|
## Memory-unsafe code
|
|
|
|
|
|
|
|
|
|
Sometimes for efficiency you may want to write low-level code that can potentially
|
2020-08-12 20:14:13 +03:00
|
|
|
|
corrupt memory or be vulnerable to security exploits. V supports writing such code,
|
2020-07-23 01:36:23 +03:00
|
|
|
|
but not by default.
|
|
|
|
|
|
|
|
|
|
V requires that any potentially memory-unsafe operations are marked intentionally.
|
|
|
|
|
Marking them also indicates to anyone reading the code that there could be
|
|
|
|
|
memory-safety violations if there was a mistake.
|
|
|
|
|
|
|
|
|
|
Examples of potentially memory-unsafe operations are:
|
|
|
|
|
|
|
|
|
|
* Pointer arithmetic
|
|
|
|
|
* Pointer indexing
|
|
|
|
|
* Conversion to pointer from an incompatible type
|
|
|
|
|
* Calling certain C functions, e.g. `free`, `strlen` and `strncmp`.
|
|
|
|
|
|
|
|
|
|
To mark potentially memory-unsafe operations, enclose them in an `unsafe` block:
|
|
|
|
|
|
2020-12-09 21:58:02 +03:00
|
|
|
|
```v wip
|
2020-07-23 01:36:23 +03:00
|
|
|
|
// allocate 2 uninitialized bytes & return a reference to them
|
2020-11-03 03:04:14 +03:00
|
|
|
|
mut p := unsafe { malloc(2) }
|
2020-07-23 01:36:23 +03:00
|
|
|
|
p[0] = `h` // Error: pointer indexing is only allowed in `unsafe` blocks
|
|
|
|
|
unsafe {
|
2020-09-19 19:18:36 +03:00
|
|
|
|
p[0] = `h` // OK
|
2020-07-23 01:36:23 +03:00
|
|
|
|
p[1] = `i`
|
|
|
|
|
}
|
|
|
|
|
p++ // Error: pointer arithmetic is only allowed in `unsafe` blocks
|
|
|
|
|
unsafe {
|
|
|
|
|
p++ // OK
|
|
|
|
|
}
|
|
|
|
|
assert *p == `i`
|
|
|
|
|
```
|
|
|
|
|
|
2020-07-29 22:41:47 +03:00
|
|
|
|
Best practice is to avoid putting memory-safe expressions inside an `unsafe` block,
|
2020-08-12 20:14:13 +03:00
|
|
|
|
so that the reason for using `unsafe` is as clear as possible. Generally any code
|
|
|
|
|
you think is memory-safe should not be inside an `unsafe` block, so the compiler
|
2020-07-29 22:41:47 +03:00
|
|
|
|
can verify it.
|
2020-07-23 01:36:23 +03:00
|
|
|
|
|
2020-08-12 20:14:13 +03:00
|
|
|
|
If you suspect your program does violate memory-safety, you have a head start on
|
|
|
|
|
finding the cause: look at the `unsafe` blocks (and how they interact with
|
2020-07-23 01:36:23 +03:00
|
|
|
|
surrounding code).
|
|
|
|
|
|
|
|
|
|
* Note: This is work in progress.
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
## Calling C functions from V
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
#flag -lsqlite3
|
|
|
|
|
#include "sqlite3.h"
|
2020-07-11 10:49:42 +03:00
|
|
|
|
// See also the example from https://www.sqlite.org/quickstart.html
|
2020-12-06 00:54:41 +03:00
|
|
|
|
struct C.sqlite3 {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct C.sqlite3_stmt {
|
|
|
|
|
}
|
2020-07-11 10:49:42 +03:00
|
|
|
|
|
2020-12-06 00:54:41 +03:00
|
|
|
|
type FnSqlite3Callback = fn (voidptr, int, &charptr, &charptr) int
|
2020-07-11 10:49:42 +03:00
|
|
|
|
|
|
|
|
|
fn C.sqlite3_open(charptr, &&C.sqlite3) int
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2020-07-11 10:49:42 +03:00
|
|
|
|
fn C.sqlite3_close(&C.sqlite3) int
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2020-07-11 10:49:42 +03:00
|
|
|
|
fn C.sqlite3_column_int(stmt &C.sqlite3_stmt, n int) int
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2020-12-18 23:31:49 +03:00
|
|
|
|
// ... you can also just define the type of parameter and leave out the C. prefix
|
2020-07-11 10:49:42 +03:00
|
|
|
|
fn C.sqlite3_prepare_v2(&sqlite3, charptr, int, &&sqlite3_stmt, &charptr) int
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2020-07-11 10:49:42 +03:00
|
|
|
|
fn C.sqlite3_step(&sqlite3_stmt)
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
2020-07-11 10:49:42 +03:00
|
|
|
|
fn C.sqlite3_finalize(&sqlite3_stmt)
|
2020-12-06 00:54:41 +03:00
|
|
|
|
|
|
|
|
|
fn C.sqlite3_exec(db &sqlite3, sql charptr, FnSqlite3Callback voidptr, cb_arg voidptr, emsg &charptr) int
|
|
|
|
|
|
2020-07-11 10:49:42 +03:00
|
|
|
|
fn C.sqlite3_free(voidptr)
|
|
|
|
|
|
|
|
|
|
fn my_callback(arg voidptr, howmany int, cvalues &charptr, cnames &charptr) int {
|
2020-12-18 23:31:49 +03:00
|
|
|
|
unsafe {
|
|
|
|
|
for i in 0 .. howmany {
|
|
|
|
|
print('| ${cstring_to_vstring(cnames[i])}: ${cstring_to_vstring(cvalues[i]):20} ')
|
|
|
|
|
}
|
2020-07-11 10:49:42 +03:00
|
|
|
|
}
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println('|')
|
|
|
|
|
return 0
|
2020-07-11 10:49:42 +03:00
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
fn main() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
db := &C.sqlite3(0) // this means `sqlite3* db = 0`
|
|
|
|
|
// passing a string literal to a C function call results in a C string, not a V string
|
|
|
|
|
C.sqlite3_open('users.db', &db)
|
|
|
|
|
// C.sqlite3_open(db_path.str, &db)
|
|
|
|
|
query := 'select count(*) from users'
|
|
|
|
|
stmt := &C.sqlite3_stmt(0)
|
2020-12-18 23:31:49 +03:00
|
|
|
|
// NB: you can also use the `.str` field of a V string,
|
|
|
|
|
// to get its C style zero terminated representation
|
2020-12-06 00:54:41 +03:00
|
|
|
|
C.sqlite3_prepare_v2(db, query.str, -1, &stmt, 0)
|
|
|
|
|
C.sqlite3_step(stmt)
|
|
|
|
|
nr_users := C.sqlite3_column_int(stmt, 0)
|
|
|
|
|
C.sqlite3_finalize(stmt)
|
|
|
|
|
println('There are $nr_users users in the database.')
|
|
|
|
|
//
|
|
|
|
|
error_msg := charptr(0)
|
|
|
|
|
query_all_users := 'select * from users'
|
|
|
|
|
rc := C.sqlite3_exec(db, query_all_users.str, my_callback, 7, &error_msg)
|
|
|
|
|
if rc != C.SQLITE_OK {
|
|
|
|
|
eprintln(cstring_to_vstring(error_msg))
|
|
|
|
|
C.sqlite3_free(error_msg)
|
|
|
|
|
}
|
|
|
|
|
C.sqlite3_close(db)
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-07-14 20:24:59 +03:00
|
|
|
|
### #flag
|
|
|
|
|
|
2020-02-08 19:00:35 +03:00
|
|
|
|
Add `#flag` directives to the top of your V files to provide C compilation flags like:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-02-08 19:00:35 +03:00
|
|
|
|
- `-I` for adding C include files search paths
|
|
|
|
|
- `-l` for adding C library names that you want to get linked
|
|
|
|
|
- `-L` for adding C library files search paths
|
|
|
|
|
- `-D` for setting compile time variables
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
You can use different flags for different targets.
|
|
|
|
|
Currently the `linux`, `darwin` , `freebsd`, and `windows` flags are supported.
|
2020-02-08 19:00:35 +03:00
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
NB: Each flag must go on its own line (for now)
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v oksyntax
|
2019-11-21 04:36:55 +03:00
|
|
|
|
#flag linux -lsdl2
|
|
|
|
|
#flag linux -Ivig
|
|
|
|
|
#flag linux -DCIMGUI_DEFINE_ENUMS_AND_STRUCTS=1
|
|
|
|
|
#flag linux -DIMGUI_DISABLE_OBSOLETE_FUNCTIONS=1
|
|
|
|
|
#flag linux -DIMGUI_IMPL_API=
|
|
|
|
|
```
|
|
|
|
|
|
2020-10-29 10:02:49 +03:00
|
|
|
|
### #pkgconfig
|
|
|
|
|
|
|
|
|
|
Add `#pkgconfig` directive is used to tell the compiler which modules should be used for compiling
|
|
|
|
|
and linking using the pkg-config files provided by the respective dependencies.
|
|
|
|
|
|
|
|
|
|
As long as backticks can't be used in `#flag` and spawning processes is not desirable for security
|
|
|
|
|
and portability reasons, V uses its own pkgconfig library that is compatible with the standard
|
|
|
|
|
freedesktop one.
|
|
|
|
|
|
|
|
|
|
If no flags are passed it will add `--cflags` and `--libs`, both lines below do the same:
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v oksyntax
|
2020-10-29 10:02:49 +03:00
|
|
|
|
#pkgconfig r_core
|
|
|
|
|
#pkgconfig --cflags --libs r_core
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The `.pc` files are looked up into a hardcoded list of default pkg-config paths, the user can add
|
|
|
|
|
extra paths by using the `PKG_CONFIG_PATH` environment variable. Multiple modules can be passed.
|
|
|
|
|
|
2020-07-14 20:24:59 +03:00
|
|
|
|
### Including C code
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
You can also include C code directly in your V module.
|
|
|
|
|
For example, let's say that your C code is located in a folder named 'c' inside your module folder.
|
|
|
|
|
Then:
|
2020-02-08 19:00:35 +03:00
|
|
|
|
|
2020-02-29 16:23:45 +03:00
|
|
|
|
* Put a v.mod file inside the toplevel folder of your module (if you
|
2020-06-14 21:20:26 +03:00
|
|
|
|
created your module with `v new` you already have v.mod file). For
|
2020-02-29 16:23:45 +03:00
|
|
|
|
example:
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v ignore
|
2020-02-29 16:23:45 +03:00
|
|
|
|
Module {
|
|
|
|
|
name: 'mymodule',
|
|
|
|
|
description: 'My nice module wraps a simple C library.',
|
|
|
|
|
version: '0.0.1'
|
|
|
|
|
dependencies: []
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-03-01 17:49:39 +03:00
|
|
|
|
|
2020-02-29 16:23:45 +03:00
|
|
|
|
* Add these lines to the top of your module:
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v oksyntax
|
2020-02-29 16:23:45 +03:00
|
|
|
|
#flag -I @VROOT/c
|
|
|
|
|
#flag @VROOT/c/implementation.o
|
2020-02-08 19:00:35 +03:00
|
|
|
|
#include "header.h"
|
|
|
|
|
```
|
2020-02-29 16:23:45 +03:00
|
|
|
|
NB: @VROOT will be replaced by V with the *nearest parent folder, where there is a v.mod file*.
|
2020-10-20 21:14:56 +03:00
|
|
|
|
Any .v file beside or below the folder where the v.mod file is,
|
|
|
|
|
can use `#flag @VROOT/abc` to refer to this folder.
|
|
|
|
|
The @VROOT folder is also *prepended* to the module lookup path,
|
|
|
|
|
so you can *import* other modules under your @VROOT, by just naming them.
|
2020-02-08 19:00:35 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
The instructions above will make V look for an compiled .o file in
|
|
|
|
|
your module `folder/c/implementation.o`.
|
2020-02-08 19:00:35 +03:00
|
|
|
|
If V finds it, the .o file will get linked to the main executable, that used the module.
|
2020-02-29 16:23:45 +03:00
|
|
|
|
If it does not find it, V assumes that there is a `@VROOT/c/implementation.c` file,
|
2020-02-08 19:00:35 +03:00
|
|
|
|
and tries to compile it to a .o file, then will use that.
|
2020-02-29 16:23:45 +03:00
|
|
|
|
|
2020-02-08 19:00:35 +03:00
|
|
|
|
This allows you to have C code, that is contained in a V module, so that its distribution is easier.
|
2020-07-14 20:24:59 +03:00
|
|
|
|
You can see a complete minimal example for using C code in a V wrapper module here:
|
|
|
|
|
[project_with_c_code](https://github.com/vlang/v/tree/master/vlib/v/tests/project_with_c_code).
|
2020-10-20 17:38:29 +03:00
|
|
|
|
Another example, demonstrating passing structs from C to V and back again:
|
|
|
|
|
[interoperate between C to V to C](https://github.com/vlang/v/tree/master/vlib/v/tests/project_with_c_code_2).
|
2020-02-08 19:00:35 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
You can use `-cflags` to pass custom flags to the backend C compiler.
|
|
|
|
|
You can also use `-cc` to change the default C backend compiler.
|
2020-02-08 19:00:35 +03:00
|
|
|
|
For example: `-cc gcc-9 -cflags -fsanitize=thread`.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-07-14 20:24:59 +03:00
|
|
|
|
### C types
|
|
|
|
|
|
2020-12-18 19:18:16 +03:00
|
|
|
|
Ordinary zero terminated C strings can be converted to V strings with
|
|
|
|
|
`unsafe { charptr(cstring).vstring() }` or if you know their length already with
|
|
|
|
|
`unsafe { charptr(cstring).vstring_with_len(len) }`.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-18 19:18:16 +03:00
|
|
|
|
NB: The .vstring() and .vstring_with_len() methods do NOT create a copy of the `cstring`,
|
|
|
|
|
so you should NOT free it after calling the method `.vstring()`.
|
2020-10-20 21:14:56 +03:00
|
|
|
|
If you need to make a copy of the C string (some libc APIs like `getenv` pretty much require that,
|
|
|
|
|
since they return pointers to internal libc memory), you can use `cstring_to_vstring(cstring)`.
|
2020-02-08 19:00:35 +03:00
|
|
|
|
|
|
|
|
|
On Windows, C APIs often return so called `wide` strings (utf16 encoding).
|
|
|
|
|
These can be converted to V strings with `string_from_wide(&u16(cwidestring))` .
|
|
|
|
|
|
|
|
|
|
V has these types for easier interoperability with C:
|
|
|
|
|
|
|
|
|
|
- `voidptr` for C's `void*`,
|
|
|
|
|
- `byteptr` for C's `byte*` and
|
|
|
|
|
- `charptr` for C's `char*`.
|
|
|
|
|
- `&charptr` for C's `char**`
|
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
To cast a `voidptr` to a V reference, use `user := &User(user_void_ptr)`.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
`voidptr` can also be dereferenced into a V struct through casting: `user := User(user_void_ptr)`.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-07-14 20:24:59 +03:00
|
|
|
|
[socket.v has an example which calls C code from V](https://github.com/vlang/v/blob/master/vlib/net/socket.v) .
|
|
|
|
|
|
|
|
|
|
## Debugging generated C code
|
2020-02-08 19:00:35 +03:00
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
To debug issues in the generated C code, you can pass these flags:
|
2020-02-08 19:00:35 +03:00
|
|
|
|
|
|
|
|
|
- `-cg` - produces a less optimized executable with more debug information in it.
|
2020-05-02 19:01:43 +03:00
|
|
|
|
- `-showcc` - prints the C command that is used to build the program.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
For the best debugging experience, you can pass all of them at the same time:
|
|
|
|
|
`v -cg -showcc yourprogram.v`,
|
|
|
|
|
then just run your debugger (gdb/lldb) or IDE on the produced executable `yourprogram`.
|
2020-02-08 19:00:35 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
If you just want to inspect the generated C code,
|
|
|
|
|
without further compilation, you can also use the `-o` flag (e.g. `-o file.c`).
|
|
|
|
|
This will make V produce the `file.c` then stop.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
If you want to see the generated C source code for *just* a single C function,
|
|
|
|
|
for example `main`, you can use: `-printfn main -o file.c`.
|
2020-05-02 19:01:43 +03:00
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
To see a detailed list of all flags that V supports,
|
|
|
|
|
use `v help`, `v help build` and `v help build-c`.
|
2020-05-02 19:01:43 +03:00
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
## Conditional compilation
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-25 23:41:22 +03:00
|
|
|
|
### Compile time if
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```v
|
2020-09-19 00:48:59 +03:00
|
|
|
|
// Support for multiple conditions in one branch
|
|
|
|
|
$if ios || android {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println('Running on a mobile device!')
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
2020-09-19 00:48:59 +03:00
|
|
|
|
$if linux && x64 {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println('64-bit Linux.')
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
2020-09-19 00:48:59 +03:00
|
|
|
|
// Usage as expression
|
|
|
|
|
os := $if windows { 'Windows' } $else { 'UNIX' }
|
|
|
|
|
println('Using $os')
|
|
|
|
|
// $else-$if branches
|
|
|
|
|
$if tinyc {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println('tinyc')
|
2020-09-19 00:48:59 +03:00
|
|
|
|
} $else $if clang {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println('clang')
|
2020-09-19 00:48:59 +03:00
|
|
|
|
} $else $if gcc {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println('gcc')
|
2020-09-19 00:48:59 +03:00
|
|
|
|
} $else {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println('different compiler')
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
2020-09-19 00:48:59 +03:00
|
|
|
|
$if test {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println('testing')
|
2020-08-20 09:33:08 +03:00
|
|
|
|
}
|
2020-09-19 00:48:59 +03:00
|
|
|
|
// v -cg ...
|
2019-11-21 04:36:55 +03:00
|
|
|
|
$if debug {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println('debugging')
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
2020-09-19 00:48:59 +03:00
|
|
|
|
// v -d option ...
|
|
|
|
|
$if option ? {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println('custom option')
|
2020-09-19 00:48:59 +03:00
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-09-25 13:02:32 +03:00
|
|
|
|
If you want an `if` to be evaluated at compile time it must be prefixed with a `$` sign.
|
2020-09-19 00:48:59 +03:00
|
|
|
|
Right now it can be used to detect an OS, compiler, platform or compilation options.
|
|
|
|
|
`$if debug` is a special option like `$if windows` or `$if x32`.
|
|
|
|
|
If you're using a custom ifdef, then you do need `$if option ? {}` and compile with`v -d option`.
|
|
|
|
|
Full list of builtin options:
|
|
|
|
|
| OS | Compilers | Platforms | Other |
|
|
|
|
|
| --- | --- | --- | --- |
|
|
|
|
|
| `windows`, `linux`, `macos` | `gcc`, `tinyc` | `amd64`, `aarch64` | `debug`, `test`, `js` |
|
|
|
|
|
| `mac`, `darwin`, `ios`, | `clang`, `mingw` | `x64`, `x32` | `glibc`, `prealloc` |
|
|
|
|
|
| `android`,`mach`, `dragonfly` | `msvc` | `little_endian` | `no_bounds_checking` |
|
|
|
|
|
| `gnu`, `hpux`, `haiku`, `qnx` | `cplusplus` | `big_endian` | |
|
|
|
|
|
| `solaris`, `linux_or_macos` | | | |
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-25 23:41:22 +03:00
|
|
|
|
### Environment specific files
|
|
|
|
|
|
|
|
|
|
If a file has an environment-specific suffix, it will only be compiled for that environment.
|
|
|
|
|
|
|
|
|
|
- `.js.v` => will be used only by the JS backend. These files can contain JS. code.
|
|
|
|
|
- `.c.v` => will be used only by the C backend. These files can contain C. code.
|
|
|
|
|
- `.x64.v` => will be used only by V's x64 backend.
|
|
|
|
|
- `_nix.c.v` => will be used only on Unix systems (non Windows).
|
2020-12-30 04:27:05 +03:00
|
|
|
|
- `_${os}.c.v` => will be used only on the specific `os` system.
|
2020-12-25 23:41:22 +03:00
|
|
|
|
For example, `_windows.c.v` will be used only when compiling on Windows, or with `-os windows`.
|
2020-12-30 04:27:05 +03:00
|
|
|
|
- `_default.c.v` => will be used only if there is NOT a more specific platform file.
|
|
|
|
|
For example, if you have both `file_linux.c.v` and `file_default.c.v`,
|
|
|
|
|
and you are compiling for linux, then only `file_linux.c.v` will be used,
|
2020-12-25 23:41:22 +03:00
|
|
|
|
and `file_default.c.v` will be ignored.
|
|
|
|
|
|
|
|
|
|
Here is a more complete example:
|
|
|
|
|
main.v:
|
|
|
|
|
```v ignore
|
|
|
|
|
module main
|
|
|
|
|
fn main() { println(message) }
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
main_default.c.v:
|
|
|
|
|
```v ignore
|
|
|
|
|
module main
|
|
|
|
|
const ( message = 'Hello world' )
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
main_linux.c.v:
|
|
|
|
|
```v ignore
|
|
|
|
|
module main
|
|
|
|
|
const ( message = 'Hello linux' )
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
main_windows.c.v:
|
|
|
|
|
```v ignore
|
|
|
|
|
module main
|
|
|
|
|
const ( message = 'Hello windows' )
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
With the example above:
|
|
|
|
|
- when you compile for windows, you will get 'Hello windows'
|
|
|
|
|
- when you compile for linux, you will get 'Hello linux'
|
2020-12-30 04:27:05 +03:00
|
|
|
|
- when you compile for any other platform, you will get the
|
2020-12-25 23:41:22 +03:00
|
|
|
|
non specific 'Hello world' message.
|
|
|
|
|
|
2020-05-26 23:39:15 +03:00
|
|
|
|
## Compile time pseudo variables
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
V also gives your code access to a set of pseudo string variables,
|
|
|
|
|
that are substituted at compile time:
|
2020-05-26 23:39:15 +03:00
|
|
|
|
|
|
|
|
|
- `@FN` => replaced with the name of the current V function
|
|
|
|
|
- `@MOD` => replaced with the name of the current V module
|
|
|
|
|
- `@STRUCT` => replaced with the name of the current V struct
|
|
|
|
|
- `@FILE` => replaced with the path of the V source file
|
|
|
|
|
- `@LINE` => replaced with the V line number where it appears (as a string).
|
|
|
|
|
- `@COLUMN` => replaced with the column where it appears (as a string).
|
|
|
|
|
- `@VEXE` => replaced with the path to the V compiler
|
|
|
|
|
- `@VHASH` => replaced with the shortened commit hash of the V compiler (as a string).
|
|
|
|
|
- `@VMOD_FILE` => replaced with the contents of the nearest v.mod file (as a string).
|
|
|
|
|
|
|
|
|
|
That allows you to do the following example, useful while debugging/logging/tracing your code:
|
|
|
|
|
```v
|
2020-12-06 00:54:41 +03:00
|
|
|
|
eprintln('file: ' + @FILE + ' | line: ' + @LINE + ' | fn: ' + @MOD + '.' + @FN)
|
2020-05-26 23:39:15 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Another example, is if you want to embed the version/name from v.mod *inside* your executable:
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v ignore
|
2020-05-26 23:39:15 +03:00
|
|
|
|
import v.vmod
|
|
|
|
|
vm := vmod.decode( @VMOD_FILE ) or { panic(err) }
|
|
|
|
|
eprintln('$vm.name $vm.version\n $vm.description')
|
|
|
|
|
```
|
|
|
|
|
|
2020-06-09 17:36:18 +03:00
|
|
|
|
## Performance tuning
|
|
|
|
|
|
2020-07-09 18:14:14 +03:00
|
|
|
|
The generated C code is usually fast enough, when you compile your code
|
|
|
|
|
with `-prod`. There are some situations though, where you may want to give
|
2020-08-24 10:04:50 +03:00
|
|
|
|
additional hints to the compiler, so that it can further optimize some
|
2020-06-09 19:09:41 +03:00
|
|
|
|
blocks of code.
|
|
|
|
|
|
|
|
|
|
NB: These are *rarely* needed, and should not be used, unless you
|
|
|
|
|
*profile your code*, and then see that there are significant benefits for them.
|
|
|
|
|
To cite gcc's documentation: "programmers are notoriously bad at predicting
|
|
|
|
|
how their programs actually perform".
|
|
|
|
|
|
|
|
|
|
`[inline]` - you can tag functions with `[inline]`, so the C compiler will
|
2020-07-09 18:14:14 +03:00
|
|
|
|
try to inline them, which in some cases, may be beneficial for performance,
|
2020-06-09 19:09:41 +03:00
|
|
|
|
but may impact the size of your executable.
|
2020-06-09 17:36:18 +03:00
|
|
|
|
|
2020-08-24 10:04:50 +03:00
|
|
|
|
`[direct_array_access]` - in functions tagged with `[direct_array_access]`
|
2020-09-19 00:48:59 +03:00
|
|
|
|
the compiler will translate array operations directly into C array operations -
|
2020-08-24 10:04:50 +03:00
|
|
|
|
omiting bounds checking. This may save a lot of time in a function that iterates
|
|
|
|
|
over an array but at the cost of making the function unsafe - unless
|
2020-12-23 20:03:56 +03:00
|
|
|
|
the boundaries will be checked by the user.
|
2020-08-24 10:04:50 +03:00
|
|
|
|
|
2020-07-09 18:14:14 +03:00
|
|
|
|
`if _likely_(bool expression) {` this hints the C compiler, that the passed
|
|
|
|
|
boolean expression is very likely to be true, so it can generate assembly
|
2020-06-09 17:36:18 +03:00
|
|
|
|
code, with less chance of branch misprediction. In the JS backend,
|
|
|
|
|
that does nothing.
|
|
|
|
|
|
2020-06-09 18:08:31 +03:00
|
|
|
|
`if _unlikely_(bool expression) {` similar to `_likely_(x)`, but it hints that
|
|
|
|
|
the boolean expression is highly improbable. In the JS backend, that does nothing.
|
|
|
|
|
|
2020-08-11 18:57:39 +03:00
|
|
|
|
<a id='Reflection via codegen'>
|
|
|
|
|
|
|
|
|
|
## Compile-time reflection
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
Having built-in JSON support is nice, but V also allows you to create efficient
|
2020-08-11 18:57:39 +03:00
|
|
|
|
serializers for any data format. V has compile-time `if` and `for` constructs:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v wip
|
2020-09-19 00:48:59 +03:00
|
|
|
|
// TODO: not fully implemented
|
2020-08-11 18:57:39 +03:00
|
|
|
|
|
|
|
|
|
struct User {
|
|
|
|
|
name string
|
|
|
|
|
age int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Note: T should be passed a struct name only
|
2019-11-21 04:36:55 +03:00
|
|
|
|
fn decode<T>(data string) T {
|
2020-04-21 06:11:50 +03:00
|
|
|
|
mut result := T{}
|
2020-08-11 18:57:39 +03:00
|
|
|
|
// compile-time `for` loop
|
|
|
|
|
// T.fields gives an array of a field metadata type
|
|
|
|
|
$for field in T.fields {
|
2020-09-28 07:13:38 +03:00
|
|
|
|
$if field.typ is string {
|
2020-08-11 18:57:39 +03:00
|
|
|
|
// $(string_expr) produces an identifier
|
|
|
|
|
result.$(field.name) = get_string(data, field.name)
|
2020-09-28 07:13:38 +03:00
|
|
|
|
} $else $if field.typ is int {
|
2020-08-11 18:57:39 +03:00
|
|
|
|
result.$(field.name) = get_int(data, field.name)
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-11 18:57:39 +03:00
|
|
|
|
// `decode<User>` generates:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
fn decode_User(data string) User {
|
2020-04-21 06:11:50 +03:00
|
|
|
|
mut result := User{}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
result.name = get_string(data, 'name')
|
|
|
|
|
result.age = get_int(data, 'age')
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Limited operator overloading
|
|
|
|
|
|
|
|
|
|
```v
|
|
|
|
|
struct Vec {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
x int
|
|
|
|
|
y int
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (a Vec) str() string {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
return '{$a.x, $a.y}'
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-21 22:20:00 +03:00
|
|
|
|
fn (a Vec) + (b Vec) Vec {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
return Vec{a.x + b.x, a.y + b.y}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-21 22:20:00 +03:00
|
|
|
|
fn (a Vec) - (b Vec) Vec {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
return Vec{a.x - b.x, a.y - b.y}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
a := Vec{2, 3}
|
|
|
|
|
b := Vec{4, 5}
|
|
|
|
|
println(a + b) // "{6, 8}"
|
|
|
|
|
println(a - b) // "{-2, -2}"
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
Operator overloading goes against V's philosophy of simplicity and predictability.
|
|
|
|
|
But since scientific and graphical applications are among V's domains,
|
|
|
|
|
operator overloading is an important feature to have in order to improve readability:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
`a.add(b).add(c.mul(d))` is a lot less readable than `a + b + c * d`.
|
|
|
|
|
|
2020-05-07 13:30:41 +03:00
|
|
|
|
To improve safety and maintainability, operator overloading is limited:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-06-27 20:26:21 +03:00
|
|
|
|
- It's only possible to overload `+, -, *, /, %` operators.
|
2020-02-08 19:00:35 +03:00
|
|
|
|
- Calling other functions inside operator functions is not allowed.
|
|
|
|
|
- Operator functions can't modify their arguments.
|
|
|
|
|
- Both arguments must have the same type (just like with all operators in V).
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
## Inline assembly
|
|
|
|
|
|
|
|
|
|
TODO: not implemented yet
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v failcompile
|
2019-11-21 04:36:55 +03:00
|
|
|
|
fn main() {
|
|
|
|
|
a := 10
|
|
|
|
|
asm x64 {
|
|
|
|
|
mov eax, [a]
|
|
|
|
|
add eax, 10
|
|
|
|
|
mov [a], eax
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-12-17 14:25:31 +03:00
|
|
|
|
## Translating C to V
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-17 14:25:31 +03:00
|
|
|
|
TODO: translating C to V will be available in V 0.3.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-17 14:25:31 +03:00
|
|
|
|
V can translate your C code to human readable V code and generate V wrappers on top of C libraries.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-12-17 14:25:31 +03:00
|
|
|
|
|
|
|
|
|
Let's create a simple program `test.c` first:
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
#include "stdio.h"
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
int main() {
|
2020-12-17 14:25:31 +03:00
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
|
|
|
printf("hello world\n");
|
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-12-17 14:25:31 +03:00
|
|
|
|
Run `v translate test.c`, and V will generate `test.v`:
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
```v
|
2020-11-03 03:04:14 +03:00
|
|
|
|
fn main() {
|
2020-12-17 14:25:31 +03:00
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
|
println('hello world')
|
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-12-17 14:25:31 +03:00
|
|
|
|
To generate a wrapper on top of a C library use this command:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
v wrapper c_code/libsodium/src/libsodium
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This will generate a directory `libsodium` with a V module.
|
|
|
|
|
|
|
|
|
|
Example of a C2V generated libsodium wrapper:
|
|
|
|
|
|
|
|
|
|
https://github.com/medvednikov/libsodium
|
|
|
|
|
|
|
|
|
|
<br>
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
When should you translate C code and when should you simply call C code from V?
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
If you have well-written, well-tested C code,
|
|
|
|
|
then of course you can always simply call this C code from V.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
Translating it to V gives you several advantages:
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
- If you plan to develop that code base, you now have everything in one language,
|
|
|
|
|
which is much safer and easier to develop in than C.
|
2020-01-16 17:29:00 +03:00
|
|
|
|
- Cross-compilation becomes a lot easier. You don't have to worry about it at all.
|
|
|
|
|
- No more build flags and include files either.
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
## Hot code reloading
|
|
|
|
|
|
2020-11-14 21:02:10 +03:00
|
|
|
|
```v live
|
2019-11-21 04:36:55 +03:00
|
|
|
|
module main
|
|
|
|
|
|
|
|
|
|
import time
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
[live]
|
|
|
|
|
fn print_message() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
println('Hello! Modify this message while the program is running.')
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
for {
|
|
|
|
|
print_message()
|
|
|
|
|
time.sleep_ms(500)
|
|
|
|
|
}
|
2019-11-21 04:36:55 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Build this example with `v -live message.v`.
|
|
|
|
|
|
|
|
|
|
Functions that you want to be reloaded must have `[live]` attribute
|
|
|
|
|
before their definition.
|
|
|
|
|
|
|
|
|
|
Right now it's not possible to modify types while the program is running.
|
|
|
|
|
|
|
|
|
|
More examples, including a graphical application:
|
2020-02-17 14:19:15 +03:00
|
|
|
|
[github.com/vlang/v/tree/master/examples/hot_code_reload](https://github.com/vlang/v/tree/master/examples/hot_reload).
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
## Cross compilation
|
|
|
|
|
|
|
|
|
|
To cross compile your project simply run
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```shell
|
2019-11-21 04:36:55 +03:00
|
|
|
|
v -os windows .
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
or
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```shell
|
2019-11-21 04:36:55 +03:00
|
|
|
|
v -os linux .
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
(Cross compiling for macOS is temporarily not possible.)
|
|
|
|
|
|
|
|
|
|
If you don't have any C dependencies, that's all you need to do. This works even
|
|
|
|
|
when compiling GUI apps using the `ui` module or graphical apps using `gg`.
|
|
|
|
|
|
|
|
|
|
You will need to install Clang, LLD linker, and download a zip file with
|
|
|
|
|
libraries and include files for Windows and Linux. V will provide you with a link.
|
|
|
|
|
|
|
|
|
|
## Cross-platform shell scripts in V
|
|
|
|
|
|
|
|
|
|
V can be used as an alternative to Bash to write deployment scripts, build scripts, etc.
|
|
|
|
|
|
|
|
|
|
The advantage of using V for this is the simplicity and predictability of the language, and
|
|
|
|
|
cross-platform support. "V scripts" run on Unix-like systems as well as on Windows.
|
|
|
|
|
|
2020-03-10 16:23:55 +03:00
|
|
|
|
Use the `.vsh` file extension. It will make all functions in the `os`
|
2019-11-21 04:36:55 +03:00
|
|
|
|
module global (so that you can use `ls()` instead of `os.ls()`, for example).
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v wip
|
2020-03-10 16:23:55 +03:00
|
|
|
|
#!/usr/local/bin/v run
|
|
|
|
|
// The shebang above associates the file to V on Unix-like systems,
|
|
|
|
|
// so it can be run just by specifying the path to the file
|
|
|
|
|
// once it's made executable using `chmod +x`.
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
rm('build/*')
|
|
|
|
|
// Same as:
|
|
|
|
|
for file in ls('build/') {
|
|
|
|
|
rm(file)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mv('*.v', 'build/')
|
|
|
|
|
// Same as:
|
|
|
|
|
for file in ls('.') {
|
|
|
|
|
if file.ends_with('.v') {
|
|
|
|
|
mv(file, 'build/')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Now you can either compile this like a normal V program and get an executable you can deploy and run
|
|
|
|
|
anywhere:
|
2020-03-10 16:23:55 +03:00
|
|
|
|
`v deploy.vsh && ./deploy`
|
|
|
|
|
|
|
|
|
|
Or just run it more like a traditional Bash script:
|
|
|
|
|
`v run deploy.vsh`
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-03-10 16:23:55 +03:00
|
|
|
|
On Unix-like platforms, the file can be run directly after making it executable using `chmod +x`:
|
|
|
|
|
`./deploy.vsh`
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-06-09 23:40:36 +03:00
|
|
|
|
## Attributes
|
|
|
|
|
|
2020-06-09 23:49:40 +03:00
|
|
|
|
V has several attributes that modify the behavior of functions and structs.
|
|
|
|
|
|
2020-10-20 21:14:56 +03:00
|
|
|
|
An attribute is specified inside `[]` right before a function/struct declaration
|
|
|
|
|
and applies only to the following declaration.
|
2020-06-10 00:04:46 +03:00
|
|
|
|
|
2020-06-09 23:40:36 +03:00
|
|
|
|
```v
|
2020-06-09 23:47:41 +03:00
|
|
|
|
// Calling this function will result in a deprecation warning
|
2020-06-09 23:40:36 +03:00
|
|
|
|
[deprecated]
|
2020-12-06 00:54:41 +03:00
|
|
|
|
fn old_function() {
|
|
|
|
|
}
|
2020-06-09 23:49:40 +03:00
|
|
|
|
|
|
|
|
|
// This function's calls will be inlined.
|
|
|
|
|
[inline]
|
2020-12-06 00:54:41 +03:00
|
|
|
|
fn inlined_function() {
|
|
|
|
|
}
|
2020-06-09 23:40:36 +03:00
|
|
|
|
|
2020-06-09 23:47:41 +03:00
|
|
|
|
// The following struct can only be used as a reference (`&Window`) and allocated on the heap.
|
|
|
|
|
[ref_only]
|
2020-06-09 23:40:36 +03:00
|
|
|
|
struct Window {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// V will not generate this function and all its calls if the provided flag is false.
|
|
|
|
|
// To use a flag, use `v -d flag`
|
|
|
|
|
[if debug]
|
2020-12-06 00:54:41 +03:00
|
|
|
|
fn foo() {
|
|
|
|
|
}
|
2020-06-09 23:40:36 +03:00
|
|
|
|
|
|
|
|
|
fn bar() {
|
2020-12-06 00:54:41 +03:00
|
|
|
|
foo() // will not be called if `-d debug` is not passed
|
2020-06-09 23:40:36 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-09 23:47:41 +03:00
|
|
|
|
// For C interop only, tells V that the following struct is defined with `typedef struct` in C
|
2020-07-09 18:14:14 +03:00
|
|
|
|
[typedef]
|
2020-12-06 00:54:41 +03:00
|
|
|
|
struct C.Foo {
|
|
|
|
|
}
|
2020-06-09 23:47:41 +03:00
|
|
|
|
|
2020-07-10 14:14:08 +03:00
|
|
|
|
// Used in Win32 API code when you need to pass callback function
|
2020-06-09 23:40:36 +03:00
|
|
|
|
[windows_stdcall]
|
2020-07-10 14:14:08 +03:00
|
|
|
|
fn C.DefWindowProc(hwnd int, msg int, lparam int, wparam int)
|
2020-06-09 23:40:36 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2020-06-26 20:36:40 +03:00
|
|
|
|
# Appendices
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
## Appendix I: Keywords
|
|
|
|
|
|
2020-09-12 14:10:27 +03:00
|
|
|
|
V has 41 reserved keywords (3 are literals):
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v ignore
|
2020-06-29 18:59:31 +03:00
|
|
|
|
as
|
2020-09-12 14:10:27 +03:00
|
|
|
|
asm
|
2020-07-08 09:50:32 +03:00
|
|
|
|
assert
|
2020-09-12 14:10:27 +03:00
|
|
|
|
atomic
|
2019-11-21 04:36:55 +03:00
|
|
|
|
break
|
|
|
|
|
const
|
|
|
|
|
continue
|
|
|
|
|
defer
|
|
|
|
|
else
|
2020-09-12 14:10:27 +03:00
|
|
|
|
embed
|
2019-11-21 04:36:55 +03:00
|
|
|
|
enum
|
2020-07-08 09:50:32 +03:00
|
|
|
|
false
|
2019-11-21 04:36:55 +03:00
|
|
|
|
fn
|
|
|
|
|
for
|
|
|
|
|
go
|
|
|
|
|
goto
|
|
|
|
|
if
|
|
|
|
|
import
|
|
|
|
|
in
|
|
|
|
|
interface
|
2020-06-29 18:59:31 +03:00
|
|
|
|
is
|
2020-09-12 14:10:27 +03:00
|
|
|
|
lock
|
2019-11-21 04:36:55 +03:00
|
|
|
|
match
|
|
|
|
|
module
|
2020-07-07 17:57:39 +03:00
|
|
|
|
mut
|
2019-11-21 04:36:55 +03:00
|
|
|
|
none
|
|
|
|
|
or
|
|
|
|
|
pub
|
|
|
|
|
return
|
2020-09-12 14:10:27 +03:00
|
|
|
|
rlock
|
|
|
|
|
select
|
2020-09-19 00:48:59 +03:00
|
|
|
|
shared
|
2020-09-12 14:10:27 +03:00
|
|
|
|
sizeof
|
|
|
|
|
static
|
2019-11-21 04:36:55 +03:00
|
|
|
|
struct
|
2020-07-08 09:50:32 +03:00
|
|
|
|
true
|
2019-11-21 04:36:55 +03:00
|
|
|
|
type
|
2020-09-12 14:10:27 +03:00
|
|
|
|
typeof
|
|
|
|
|
union
|
2020-07-08 09:50:32 +03:00
|
|
|
|
unsafe
|
2019-11-21 04:36:55 +03:00
|
|
|
|
```
|
2020-07-08 09:50:32 +03:00
|
|
|
|
See also [Types](#types).
|
2019-11-21 04:36:55 +03:00
|
|
|
|
|
|
|
|
|
## Appendix II: Operators
|
|
|
|
|
|
2020-07-23 19:55:54 +03:00
|
|
|
|
This lists operators for [primitive types](#primitive-types) only.
|
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
|
```v ignore
|
2019-11-21 04:36:55 +03:00
|
|
|
|
+ sum integers, floats, strings
|
|
|
|
|
- difference integers, floats
|
|
|
|
|
* product integers, floats
|
|
|
|
|
/ quotient integers, floats
|
|
|
|
|
% remainder integers
|
|
|
|
|
|
2020-10-03 15:07:48 +03:00
|
|
|
|
~ bitwise NOT integers
|
2019-11-21 04:36:55 +03:00
|
|
|
|
& bitwise AND integers
|
|
|
|
|
| bitwise OR integers
|
|
|
|
|
^ bitwise XOR integers
|
|
|
|
|
|
2020-10-03 15:07:48 +03:00
|
|
|
|
! logical NOT bools
|
|
|
|
|
&& logical AND bools
|
|
|
|
|
|| logical OR bools
|
|
|
|
|
!= logical XOR bools
|
|
|
|
|
|
2019-11-21 04:36:55 +03:00
|
|
|
|
<< left shift integer << unsigned integer
|
|
|
|
|
>> right shift integer >> unsigned integer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Precedence Operator
|
|
|
|
|
5 * / % << >> &
|
|
|
|
|
4 + - | ^
|
|
|
|
|
3 == != < <= > >=
|
|
|
|
|
2 &&
|
|
|
|
|
1 ||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Assignment Operators
|
|
|
|
|
+= -= *= /= %=
|
|
|
|
|
&= |= ^=
|
|
|
|
|
>>= <<=
|
|
|
|
|
```
|