Despite being a very new and constantly evolving programming language, V has got all the most sought-after features that satisfy the needs of modern-day programmers. In this section, we will explore various features of V.
Performance
V has Clang, GCC, or MSVC as its primary backend, depending on the OS, which allows it to compile to human-readable C. Having these compilers as the main backend allows V to have easy interoperability with C. V, with its innovative memory management, performs a minimal amount of memory allocation by using value types and string buffers. A program written in V gets compiled to native binaries without any dependencies. Also, V compiles the whole application into a single binary, which makes it easy to deploy.
Speed
At the time of writing this book, according to the official website, https://vlang.io/, with a Clang backend, V compiles ~110k LOCs per second, per CPU core. With x64 and a TCC backend, V compiles ~1 million LOCs per CPU core.
No null values
A null value indicates nothing. A null value neither represents an empty nor a default value. Having null values in a programming language enforces you to handle the null scenarios using multiple checks. These checks, when missed, might lead to errors.
V does not have null or nil values, unlike other programming languages such as Java, C#, Python, or Go. This is because all the types in V are zeroed in by default. Zeroed in means that they are assigned with default values, such as an empty string for string types, 0 for integers, and false for Boolean types. Thus, V does not rely on the compiler to check whether the type is null or not, thereby preventing the program from creating several errors.
No global variables
Global variables allow you to maintain the state at the application level. Though this sounds comforting, global variables slowly lead to reliability problems that arise due to the growing number of actors on such variables.
In V, global variables are disabled by default. These global variables can be declared using the __global
keyword and running the V program with the -enable-globals
argument. The reason why V facilitates working with global variables is to allow the implementation of low-level applications such as programming OS kernels or system drivers. In such cases, you may need to have variables that can be accessed globally.
No undefined values
In V, when you declare a variable of any type, you must initialize it. Otherwise, it leads to compilation errors. Also, in the case of structs, which are detailed in Chapter 8, Structs, the fields of a struct are zeroed into their default values.
Error handling
V has a very simple approach to dealing with errors. You have the flexibility to deal with these errors using an or {}
block or let the errors propagate using the optional operator, ?
. You can also build custom errors using the built-in error method, which accepts a string as an input argument. The different ways to deal with errors will be demonstrated in the Functions can have optional return types section of Chapter 7, Functions.
Powerful concurrency
V has a very powerful concurrency framework. It is essential for an application running on a high-end computing device to be able to utilize its resources, such as its CPU cores, efficiently. Through V's built-in concurrency model, using the go
keyword, you can spawn functions to run concurrently on other threads, different from the thread where the main program runs. The functions that run concurrently are called coroutines.
You can have shared variables to synchronize the data between coroutines by enforcing read-only locks using the rlocks
keyword or read/write/modify locks using the lock
keyword. This approach is demonstrated in the Sharing data between the main thread and concurrent tasks section of Chapter 10, Concurrency. With this traditional concurrency synchronization technique, the coroutines communicate by sharing data or memory.
As creating shared variables and manually enforcing locks is often cumbersome, V has a built-in library called sync
that implements advanced concurrency patterns known as channels. A channel allows you to share data by establishing a communication channel among coroutines. A channel acts as a medium where a coroutine pushes data into it and other channels pop the data out of it. We will learn about channels, along with their features and how to work with buffered and unbuffered channels, in Chapter 11, Channels – An Advanced Concurrency Pattern.
Easy cross-compilation
V allows you to generate cross-platform binaries with its cross-platform compilation capabilities. With this feature, from a *nix OS, you can generate your application's executable that targets *nix OS variants, as well as Windows or macOS. From a *nix OS, let's say Ubuntu, create a file named hello.v
and add the following code to it:
module main
fn main() {
os := $if windows { 'Windows' } $else { 'Unix' }
println('Hello, $os user!')
}
The $
symbol in the preceding code tells the compiler to evaluate the following if
condition right away during compile time. Also, windows
is a built-in term that's used to identify the OS type.
Run the preceding code using the v run hello.v
command. You will see Hello, Unix user!
as the output.
From the *nix OS, you can run the following command to create a cross-compiled executable targeting the Windows OS.
Before you start generating a cross-compiled binary for the hello.v
program, you need to install mingw-64
, which is required to generate an executable targeting the Windows OS. To install mingw-64
, run the following command:
sudo apt install -y mingw-w64
Alternatively, you can try sudo apt install -y mingw-w64
on Debian-based distributions or sudo pacman -S mingw-w64-gcc
on Arch.
Once mingw-64
has been installed, run the following command from the Ubuntu OS to generate the executables that can run on the Windows OS, as follows:
v -os windows hello.v
The preceding command will generate an executable named hello.exe
. Now, transfer the .exe
file to the Windows OS. Running the executable from Command Prompt will output Hello, Windows user!
.
You can also cross-compile to generate *nix binaries from a Windows OS. All you need to do is install Clang for Windows, as described at https://clang.llvm.org/get_started.html, and run the following command, which generates the *nix binary:
v -os linux hello.v
Similarly, to generate an executable for macOS, run the following command:
v -os macos hello.v
V to JavaScript conversion
In addition to C as a primary backend, V also has JavaScript and WASM backends. V programs can be translated into JavaScript. To translate the hello.v
into JavaScript, you can run the following command:
v -o hello.js hello.v
It is as simple as the preceding command. The outcome will produce a JavaScript file named hello.js
that reflects the functionality written in the hello.v
program.
Profiling
V has an built-in profiling tool that you can use to analyze how your program is behaving or how many times a function gets called on average by a function per call. You might need this information to debug and optimize the application code. To run the profiler against the V program, let's say hello.v
, run the following command:
v -profile profile.txt hello.v
Notice the usage of the -profile
argument, followed by the text file. Running the preceding command generates a binary for the hello.v
program. Running the binary generates profile.txt
with a detailed list of all the function calls with three columns. Each of the columns in the text file represents the number of calls, average time per call, and total time per call.