KYGnus ( Koosha Yeganeh Gnu Linux Softwares )
• V is a statically typed and compiled programming language
• Simple and maintainable syntax
• Backward compatibility, stability, and easy to upgrade to future versions4 Introduction to V Programming
• Features of V programming
• V as a framework
• Operating systems V supports
main repository of V programing
V has an active community of developers and contributors. The community is highly
active and responsive to issues raised on GitHub. You can participate in discussions at
https://github.com/vlang/v/discussions and the team is also available on
Discord: https://discord.gg/vlang
1 - Performance
2 - Speed
3 - No null values
4 - No global variables
5 - No undefined values
6 - Error handling
7 - 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.
8 - 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:
Debian Base systems
sudo apt install -y mingw-w6410
Introduction to V Programming Alternatively, you can try
sudo apt install -y mingw-w64
distributions or
Arch Base
sudo pacman -S mingw-w64-gcc
RedHat Base Like Fedora
sudo yum install mingw32-gcc-c++
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
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.
V has a 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
Note : 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.
With the suite of packages V comes with, it can be considered equivalent to a framework.
A framework generally comprises all the features of full-blown programming, along
with the ability to smoothly plug and play the external packages. Using V, you can write
enterprise-grade software, even though it is still in development.
The vweb web server is a built-in library. Though it is in an alpha state at the time of
writing this book, it offers various features in its current state, including the following:
• Built-in routing.
• Handling parameters.
• Templating engine.
• Very fast performance, like C on the web.
• Building the project using vweb generates a single binary, thus simplifying
deployments.
V has a cross-platform ui library. Using this library, you can leverage the power of building
cross-platform GUI applications. The ui library can be found at the official GitHub
repository at https://github.com/vlang/ui, which is licensed under GPL 3.0.
V has a ui module that uses native GUI toolkits: WinAPI/GDI+ on Windows and Cocoa
on macOS. On Linux, custom drawing is used.
Arch:
sudo pacman -S libxi libxcursor mesa
Debian/Ubuntu:
sudo apt install libxi-dev libxcursor-dev mesa-common-dev
Fedora:
sudo dnf install libXi-devel libXcursor-devel mesa-libGL-devel
Install ui
v up
v install ui
Basic app
vi basic.v
import ui
struct App {
mut:
window &ui.Window = unsafe { nil }
first_name string
last_name string
}
fn main() {
mut app := &App{}
app.window = ui.window(
width: 600
height: 400
title: 'V UI Demo'
children: [
ui.row(
margin: ui.Margin{10, 10, 10, 10}
children: [
ui.column(
width: 200
spacing: 13
children: [
ui.textbox(
max_len: 20
width: 200
placeholder: 'First name'
text: &app.first_name
),
ui.textbox(
max_len: 50
width: 200
placeholder: 'Last name'
text: &app.last_name
),
]
),
]
),
]
)
ui.run(app.window)
}
v run basic.v
inix is an effort to write a modern, fast, and useful OS using V. Vinix is purposefully
built to facilitate writing low-level software.
The Vinix OS is licensed under GPL 2.0, and you can find its entire source code on its
official GitHub repository at https://github.com/vlang/vinix. You can always
download the latest version of the Vinix OS in the form of ISO from the official link:
https://builds.vinix-os.org/repos/files/vinix/latest/vinix.iso.
Vinix aims to have the following features:
• Make a usable OS that can run on emulators, virtual machines, and physical hardware
• Target modern 64-bit architectures, CPU features, and multi-core computing
• Maintain good source-level compatibility with Linux, which helps with porting programs between Vinix and Linux
• Explore V’s capabilities in bare-metal programming
• Improve the compiler in response to the uncommon needs of bare-metal programming
Download the V-installer file and run your Linux operating system. This file will automatically install the V language and everything you need to work with it.
./V-installer
x := 10
mut x :=10
• A variable name can only start with the lowercase alphabet.
• A variable name cannot contain the uppercase alphabet.
• Special characters are not allowed except for underscores.
• Variable names can end with numbers and underscores.
• Lengthy variable names can have words separated with _ to enhance readability
and consistency across the code. This type of variable naming convention is often
referred to as snake_case.
• := is a colon followed by an equals sign and is used to declare a variable in V.
• = is an equals sign used to reassign a value to the already declared mutable
variables only.
a, b, c := 3, 4, 5
mut i, mut j := 'Hi', 'Hello'
mut <variable_name> := <initializing_value>
The syntax to update the mutable variable is as follows:
<variable_name> = <updated_value>
mut i := 10
i = 100
<variable_name> := <initializing_value>
msg := 'Hello'
Note : Any variable that is declared but unused will lead to a warning or error depending on the
mode in which you run the program
• Global variables are not allowed in V.
• The redeclaration or redefinition of variables is not allowed in V.
• Variable shadowing is not allowed in V
V only allows you to define variables within functions and scripts-
module main
fn os() {
msg := 'GnuLinux is Best'
println(msg)
}
fn programing() {
msg := 'Vlang is Best'
println(msg)
}
fn main() {
os)
programing()
}
• The name of a constant can only start with the lowercase alphabet.
• The name of a constant cannot contain the uppercase alphabet.
• Special characters are not allowed except underscores.
• The name of a constant can end with numbers and underscores.
• Lengthy constant names can have words separated with _ to enhance readability
and consistency across code. This type of naming convention is often referred toas snake_case.
const <constant_name> = <constant_value>
const username = "koosha"
const password = "123123"
or
const {
username = "koosha"
password = "123123"
}
const koosha = programing{
python:90
bash:90
v:30
}
Notice that we have defined a constant named greet inside the function that doesn’t
compile. Therefore, the preceding code will fail with a message that says error: const
can only be defined at the top level (outside of functions).
module mod1
const greet_count = 5
pub fn do_work() {
println(mod1.greet_count)
}
how to determine the type of
any variable using the built-in typeof() function as follows:
typeof(variable).name
completed := true
&& > Logical AND
|| > Logical OR
! > Logical NOT
x := 1
typeof(x).name // int
i := 1_000
j := 1000
println(i == j) // true
module main
fn demo() {
h1 := 0x64
b1 := 0b1100100
o1 := 0o144
// hexadecimal starts with 0x
// binary starts with 0b
// Octal starts with 0o
println('Value of var h1 with hexadecimal value : $h1')
println('Data type of var h1 with hexadecimal
value : ${typeof(h1).name}')
println('Value of var b1 with binary value : $b1')
println('Data type of var b1 with binary value : ${typeof(b1).name}')
println('Value of var o1 with octal value : $o1')
println('Data type of var o1 with octal value : ${typeof(o1).name}')
}
fn main() {
demo()
}
To start with, we have integer types i8, i16, int (32-bit integer), and i64, and unsigned
integer types byte (which is referred to as u8), u16, u32, and u64.
Signed integers support both positive and negative ranges of values, typically to represent
whole numbers. Unsigned integers don’t represent any sign and are non-negative
numbers. The following table indicates the range of all the integer types:
Type Min Max
i8 -128 127
i16 -32768 32767
i32 -2147483648 2147483647
i64 i64 9223372036854775807
byte(u8) 0 255
u16 0 65535
u32 0 4294967295
u64 0 18446744073709551615
In the preceding table, notice that the minimum value for i64 is represented as
i64(-9223372036854775807 – 1) because the C compilers parse literal values without
sign and 9223372036854775808 overflows the maximum value possible for i64, which
is 9223372036854775807. Ideally, there is no bigger type beyond i64 that holds a value
greater than 9223372036854775807. Hence, we represent the minimum value as the result
of an evaluation of subtracting 1 from -9223372036854775807.
<VARIABLE_NAME> := ‘
h := 'hello'
println(h) // hello
println(h.len) // 5
typeof(h).name // string
username := "koosha"
println(typeof(username[0]).name) // byte
println(username[0])
Strings are immutable by default
mut msg := 'Hello Bro!'
msg = 'Hope you are doing good.'
println(msg)
mut msg := 'Hello Koosha!'
msg += 'VLang Developer'
println(msg) // Hello koosha VLang Developer
a := 'con'
b := 'cat'
println(a + b) // concat
i := 1
j :='man army'
println('$i $j')
a :='KYGnus'
b := a.substr(0,3)
println(b) // KYG
sp :='The tiny tiger tied the tie tighter to its tail'
res := sp.split(' ') // split by space as delimiter
println(typeof(res).name) // []string
println(res) // // ['The', 'tiny', 'tiger', 'tied', 'the',
//'tie', 'tighter', 'to', 'its', 'tail']
sp := 'The tiny tiger tied the tie tighter to its tail'
println(sp.count('t')) // 10
println(sp.count('T')) // 1
println(sp.count('tie')) // 2
println(sp.count('-')) // 0
module main
fn main() {
hs := 'monday'
if hs.contains('mon') {
println('$hs contains mon')
} else {
println('$hs does not contains mon')
}
}
mut friends := [ 'Mahdi_Bahadori', 'Hamed_Fard', 'Farhad_Mehryari' , 'sahand_Alipoor' , 'Amin_Pirmohamadi' ]
mut <VAR_NAME> := []DATA_TYPE{}
mut users := []string{}
println(useers) //prints empty array: []
Now, we will append a value to the mutable array of animals strings:
users << 'Koosha'
users << 'Farhad'
println(users) // ['Koosha', 'Farhad']
mut i := []int{len:3}
println(i)
output : [0, 0, 0]
mut user := []string{len: 3, init: 'koosha'}
mut pass := []int{len: 3, init: 1}
println(user)
println(pass)
mut k := []int{cap: 2}
println(k)
mut legendary_men := ['Ken Thompson', 'Richard Stallman', 'Linus Torvalds' , 'Dennis Ritchie' , 'Steve Wozniak' , 'Amir Emad Mirmirani-jadi']
println(legendary_men)
println(legendary_men.len) // Length of sports array
println(legendary_men.cap) // Capacity of sports array
legendary_men.delete(2)
legendary_men << [ 'Bram Moolenaar' , 'Guido Van Rossum' ]
println(legendary_men)
println(legendary_men.len)
s := legenadry_men[1]
println(s) // Ken Thompson
ARRAY_VAR[STARTING_INDEX … ENDING_POSITION]
println(legenadry_men[1..3]) // prints ['Ken Thompson', 'Linus Torvalds']
n the preceding code, we slice the sports array to print the elements starting from
index 1 to position 3. Notice the terms I have used here; the first number in the [1…3]
range is 1, which indicates the index, and the other number, 3, indicates the position.
1 -
odd := [1, 3, 5, 7]
println(3 in odd) // prints: true
println(8 !in odd) // prints: true
2-
mut legendary_men := ['Ken Thompson', 'Richard Stallman', 'Linus Torvalds' , 'Dennis Ritchie' , 'Steve Wozniak' , 'Amir Emad Mirmirani-jadi']
println('Ken Thompson' in legendary_men)
println('Ken Thompson' !in legendary_men)
mut fix := [4]int{}
println(fix) // [0, 0, 0, 0]
fix[1] = 33
println(fix) //[0, 33, 0, 0]
s := fix[1..]
println(s) // [33, 0, 0]
myarr := [1,2,3,4],5,6
mut arr := myarr.clone() // copies the array myarr to arr
println(arr)
In the preceding code, we can observe that the r immutable array is cloned to an
u mutable array.
The direct assignment of an array to a new variable will throw an error. For example,
let’s consider what happens when we try to run the following code:
s := myarr // This will throw error.
The program will throw an error with the message that says error: use array2 :=
array1.clone() instead of array2 := array1 (or use unsafe).
Another way to copy an array, as suggested in the previous error message, is to copy an
array to a new variable using unsafe:
s := unsafe { myarr }
println(s)
mut i := [ 3, 2, 8, 1]
i.sort() // ascending order
println(i)
i.sort(a > b) //descending order
println(i)
mut os := ['Linux', 'BSD', 'Mac', 'Windows']
os.sort() // ascending order
println(os)
os.sort(a>b) // reverse order
println(os)
f := [1, 2, 3, 4, 5, 6, 7, 8, 9]
multiples_of_3 := f.filter(it % 3 == 0)
println(multiples_of_3) // [3, 6, 9]
fruits := ['apple', 'mango', 'water melon', 'musk melon']
fruits_starting_m := fruits.filter(fn (f string) bool {
return f.starts_with('m')
})
println(fruits_starting_m)
visitor := ['Tom', 'Ram', 'Rao']
res := visitor.map('Mr. $it')
println(res)
mut MAP_NAME := map[KEY_TYPE]VALUE_TYPE{}
MAP_NAME[KEY_1] = VALUE_1
MAP_NAME[KEY_2] = VALUE_2
.
.
MAP_NAME[KEY_N] = VALUE_N
mut users := map[string]int{}
or
mut users := map[string]string{}
users['Linux'] = "Koosha"
users['Linux'] = 123123
mut MAP_NAME := {
KEY_1: VALUE_1
KEY_2: VALUE_2
.
.
KEY_N: VALUE_N
}
mut users := {
'Linux' : 'koosha'
'MSwindows' : 'Mahdi Bahadori - AminPrimohamadi - Masood Tavakoli'
'macOS' : 'Farhad Mehryari'
}
println(users)
cnt := users.len
println('len of users Defined is Map is $cnt')
println(users['Linux']) // 'koosha'
users['Linux'] = 'Koosha - Farhad Mehryari'
println(users)
users.delete('macOS')
if CONDITION_1 {
// CONDITION_1 evaluated to true
} else if CONDITION_2 {
// CONDITION_2 evaluated to true
} else {
// None of the Conditions evaluated to true
}
fn check_os(os string){
if os == "Fedora"{
println("Fedora From RedHat")
}else if os == "Ubuntu"{
println("Ubuntu from canonical")
}else if os == "openSUSE"{
println("openSUSE from SUSE company")
}else{
println("can not Detect os company")
}
}
fn main(){
check_os("Fedora")
}
V allows you to label code and refer to the execution control using the goto keyword. The
goto statement needs to be specified with a label that indicates the control to navigate to
the label when the execution flow encounters a goto statement. A label is defined with
general text followed by a : colon symbol.
Note
A goto statement must be wrapped inside an unsafe block. This is because
goto allows the program execution flow to move past variable initialization
or return to code that accesses memory that has already been freed. As the
goto statement requires an unsafe block, it should be avoided to prevent
the risk of violating memory safety.
sample_label:
println(‘this will be called when goto is invoked’)
unsafe {
goto sample_label
}
module main
import os
fn main() {
main_linux:
inp := os.input('Enter Linux Name : ')
if inp != 'MSwindows' {
linux := inp.str()
if linux == 'Fedora' || linux == 'Ubuntu' {
println('os is Linux and Ubuntu or Fedora')
} else if linux == 'openSUSE' {
println('OS is Server Linux From Germany : openSUSE Leap')
} else {
println('Can not Detect OS')
}
} else {
unsafe {
goto main_linux
}
}
}
fn get_user(username string) {
match username {
'Koosha_Yeganeh' {
println('V and python Developer')
}
'Farhad_Mehryari' {
println('Nodejs and TypeScript Developer')
}
'Peyman_Ramazani' {
println('Delphi Developer')
}
'Masood_Tavakoli' {
println('asp.net Developer')
}
else {
println('The entered username is not in my developer friends group')
}
}
}
fn main() {
get_user('Koosha_Yeganeh')
}
enum Dist {
fedora
ubuntu
rocky
debian
}
fn get_os(os Dist) string {
return match os {
.fedora {
'Best Desktop Linux For Enterprise Users'
}
.ubuntu {
'Best Linux for Starting'
}
.rocky {
'Best rpm Linux For Servers'
}
.debian {
'best deb Linux for Servers'
}
}
}
fn main() {
fedora := get_os(Dist.fedora)
println(fedora)
ubuntu := get_os(Dist.ubuntu)
println(ubuntu)
}
fn main() {
age := 18
res := match age {
0...18 { 'Person with age $age classified as a Child' }
19...120 { 'Person with age $age classified as an Adult' }
else { '$age must be in the range 0 to 120' }
}
println(res)
}
for INDEX_VAR, VALUE_VAR in COLLECTION {
// access each element’s index and its value
}
fn main() {
os := {
'RedHat': 'Fedora'
'Debian': 'Ubuntu'
'SUSE' : 'openSUSE Leap'
}
for operation, distro in os {
println('os is ${operation} and distribution is ${distro}')
}
linux := ['Fedora', 'Ubuntu', 'openSUSE']
for idx, ele in linux {
println('${idx} - ${ele}')
}
}
In the preceding code, the for loop iterates over an array named linux with the values of a string data type
The idx variable will always be
of the integer data type and signifies the index of each element, starting from 0, and increments by 1 for every iteration. The ele variable holds the value of the element for that iteration
module main
fn main() {
col := [1, 2, 3, 4, 5, 6, 7]
for val in col {
if val % 2 == 0 {
println('$val is Even')
} else {
println('$val is Odd')
}
}
}
module main
fn main() {
sample := [3, 4, 23, 12, 4, 1, 45, 12, 42, 17, 92,
38]
for i := 0; i < sample.len; i += 3 {
println(sample[i])
}
}
fn main() {
for val in 0 .. 4 {
println(val)
}
}
fn main() {
mut count := 1
for {
println('Hi $count times')
count += 1
}
}
fn main() {
mut count := 0
input := os.input('Enter number of times to Greet:')
limit := input.int()
for {
if count >= limit {
break
}
println('Hi')
count += 1
}
println('Greeted Hi $count times')
}
fn main() {
for i in 0 .. 10 {
if i % 2 == 0 { // skips printing number
// that is a multiple of 2
continue
}
println(i)
}
module main
fn main() {
greet := fn (name string) {
println('Hello, $name')
}
greet('Jadi')
greet('Hasan Azimi')
}
fn hello_back()string{
return "Hello New User"
}
import os
fn say_hello() string {
username := os.input('Enter username : ')
password := os.input('Enter Password : ')
if username == 'koosha' && password == '102030' {
return 'You are Logined'
} else {
return 'Login ERROR'
}
}
fn main() {
ut name := say_hello()
println(name)
}
fn adder(i int, j int) int {
return i + j
}
fn subtractor(i int, j int) int {
return i - j
}
fn multiplier(i int, j int) int {
return i * j
}
fn main(){
mut result := subtractor(2023,1991)
println("Linux Born at : ${result}")
fn get_os(name string) string {
return 'Hello ${name}'
}
fn main() {
res := get_os('koosha')
println(res)
}
fn add(a int, b int) int {
return a + b
}
res := add(2, 4)
println(res)
fn greet_and_message_length(name string) (string, int) {
mut greeting := 'Hello , ' + name + '!'
return greeting, greeting.len
}
i, j := greet_and_message_length('Fedora')
println(i)
println(j)
fn get_input_len(username string, password string) int {
data := username + password
return data.len
}
fn main() {
length := get_input_len('koosha', '102030')
println(length)
}
i, _ := greet_and_message_length('Fedora')
fn greet(p string) string {
return 'Hello, $p!'
}
fn welcome(p string) string {
msg := 'Nice to meet you!'
mut g := greet(p)
g = g + ' $msg'
return g
}
fn main(){
res := welcome('Visitor')
println(res)
}
fn increment_array_items(arr []int, inc int) []int {
mut tmp := arr.clone()
for mut i in tmp {
i += inc
}
return tmp
}
a := [5, 6]
res := increment_array_items(a, 100)
println('a: $a')
println('res: $res')
#!/usr/local/bin/v run
cnt := 2
for i in 0 .. cnt {
log('iteration $i')
}
fn log(msg string) {
println(msg)
}
functions in V are pure by default. This means that functions can only process the arguments they are passed with and return the processed output. The functions cannot access variables that are defined outside of the function body.
However, V enables us to declare global variables. When it comes to the implementation of low-level applications such as programming OS kernels or system drivers, you might need to have the variables accessed globally. In such situations, you can declare variables as global and run the V program with the -enable-globals argument.
Now, we will examine how to declare global variables and run V programming using the -enable-globals flag. The code that we are going to work on will be structured in the following directory hierarchy:
The syntax to create a global variable in V is as follows:
__global(
<variable_name> <data_type>
)
// file: mymod/mymod.v
module mymod
__global (
msg string
)
In the mymod.v file, we defined a msg global variable of the string data type. Fromthe command-line Terminal, navigate to the parent directory where we have main.v located. To run main.v, you need to provide -enable-globals as an argument to V, as follows:
v -enable-globals run main.v
You will see the following output:
global variable demo
By default, all of the functions declared in V are private and are only accessible within the default scope that they are defined in. To expose the methods to the outside world, they are decorated with the pub keyword, which denotes public access.
// file: mod/mod.v
module mod
fn get_name() string {
return 'Hello to Koosha Yeganeh'
}
pub fn get_username() string {
return 'username of Fedora is KYGnus'
}
pub fn get_test() string {
hello := get_name()
return 'username : ' + hello + 'user and pass is Test'
}
import mod
test := mod.get_test()
g := mod.get_username()
println(g)
println(test)
module main
fn void_func_defer() {
println('Hello')
defer {
println('Hi from defer block')
}
println('How are you?')
// the defer block will be executed when the
// execution control reaches here
}
fn main() {
void_func_defer()
}
Result :
Hello
How are you?
Hi from Defer Block
Structs in V allow you to define composite objects. They facilitate you to create complex data structures, allowing its fields to have different data types. The fields of the struct can be primitive data types, enums, or could be another struct.
struct Linux_Users {
username string
password int
id string
os_hash int
}
fn main() {
fedora := Linux_Users{
username: 'koosha'
password: 123123
id: 'main_user'
os_hash: 159753123654
}
println('username is ${fedora.username} and password is : ${fedora.password}')
}
When a struct is initialized, its memory is allocated on the stack by default. V allows you to initialize a struct and allocates its memory on the heap, provided that you specify & before the struct name during initialization, as shown here:
Stack vs Heap: What’s the difference?
Stack vs Heap Memory Allocation
n1 := &Linux_users{'Farhad', 1 , 'Unix Main User' , 123456}
Heap structs are particularly useful when dealing with structs that carry large amounts of data. Therefore, opting for heap structs can reduce explicit memory allocations
struct Users {
mut:
username string
password int
id int
}
mut fedora := Users{
username: 'Koosha'
password: 123123
id: 100
}
println(fedora.username)
fedora.username = 'kooshakooshadv'
println(fedora.username)
• The variable to which the struct is declared must be mutable.
• You must specify the struct name to the right-hand side of the = operator followed
by the struct fields that you wish to update. The values must be enclosed in curly
braces ({}).
• The field names must be specified as literals and only then can they be assigned
values separated by a : sign.
• The unspecified fields in the update statements are assigned zero by default.
A struct allows the filtering of its fields at various access levels. With the help of the keywords pub, for public access of fields, and mut, to indicate mutable fields, you can control the way the fields of a struct can be accessed.The various access control levels that could be applied to the fields of a struct are tabulated here:
pub struct Note {
pub:
id int
pub mut:
message string
status bool
}
We have marked the Note struct as pub. We also marked the id field as public by declaring it under the pub group. The fields under the pub group are public and are read-only outside the module. Also, the message and status fields are marked with the pub and mut access modifiers. Defining struct fields under the pub mut group indicates that the struct fields are accessible and mutable within and outside the module
where Note is defined.
pub struct Note {
pub:
id int
pub mut:
message string
}
mut name := Note{
id: 123
message: 'Hello New User'
}
println(name.id)
struct Users {
pub mut:
username string
password int
mut:
text string
id int
}
n := Users{
username: 'Fedora'
password: 789
text: 'Fedora Workstation From RedHat'
id: 555
}
println(n.username)
pub struct Note {
pub:
id
int
pub mut:
message string [required]
status bool
}
struct Users {
pub mut:
username string [required]
password int
mut:
text string
id int
}
n := Users{
username: 'Fedora'
password: 789
text: 'Fedora Workstation From RedHat'
id: 555
}
println(n.username)
import time
pub struct Note {
pub:
id
int
created time.Time = time.now()
pub mut:
message string [required]
status bool
due
time.Time = time.now().add_days(1)
}
fn (r RECEIVER_TYPE) METHOD_NAME(OPTIONAL_INPUT_ARGUMENTS)
RETURN_TYPE {
METHOD BODY
}
module <module_name>
import <module_name>
• The directory name must match the module name.
• Imported modules must be consumed in the code.
• Multiple V files in the module must define the same module.
• Both private and public members of a module can be accessed from anywhere within the module.
• Only public members of the module are accessible outside of the module.
• Cyclic imports are not allowed.
• Define init functions to execute one-time module-level initializer functionality.
We will create a module in our modulebasics project.
1- The first principle to note when creating a module is that the Directory name must match the module name. Failing to have a similar name for the directory and module will lead to an error with a message that says builder error: bad module definition.Let’s create a module named mod1 by running the following command from the Terminal:
v new app
mkdir login
module login
import os
pub fn login() string {
username := os.input('Enter username : ')
password := os.input('Enter Password : ')
if username == 'Koosha' && password == 'K102030k' {
return 'Logined'
} else {
return 'Login ERROR'
}
}
cat src/main.v
module main
import login
fn main() {
login_name := login.login()
println(login_name)
}
v run .
If there are multiple files in a module, then all of the files must define the same module definition. Failing to define the same module for all the files within a module will lead to an error that is similar to builder error: bad module definition.
Modules in V allow you to define a function named init that is automatically executed when you import the module where it is defined. The init functions of a module act as an initializer of certain functionality, such as establishing database connections or initializing C libraries or module-specific settings,if any. To define the initializer function, you must meet the following conditions:
• The init function must only be defined once inside the module.
• The init function must not be marked as public.
• The init function must not accept any input arguments.
• The init function must not have a return type.
module main
import os
pub fn dist()string{
os_name := "Fedora"
println(os_name)
}
fn init(){
println("Hello from init Function")
}
// file : mod1/file1.v
module mod1
pub const vmsg = 'Hello From V Lang'
// file: modulebasics.v
module main
import mod1
fn main() {
println(mod1.vmsg)
}
Concurrency means running tasks concurrently. While this might seem like a very
abstract definition, let’s consider the following real-world example. You wake up in the
morning of winter, and you need hot water to bathe. You can only bathe when the water
is hot enough. However, you have other morning chores to finish off while the water gets
hot. So, you turn on the water heater and then, let’s say, you brush your teeth for some
time while the water heater indicates the water is hot. Then, you switch off the water
heater, enjoy a hot shower, and get ready for the day
Understanding the time module
import time
The time module in V has a vast number of functionalities, including telling the current
time on the system using the time.now() expression. If we are just interested in the
hours, minutes, and seconds part of the time at the time of execution, you can write the
corresponding expression as
time.now().hhmmss()
sleep
import time
time.sleep(0.5 * time.second)
stopwatch
sw.elapsed().nanoseconds()
In the aforementioned syntax, the sw variable holds the instance of initiation of the
stopwatch using time.new_stopwatch(). The duration of time that’s elapsed
is in nanoseconds, which is of the i64 type. You can have the elapsed time in
other units instead of calling nanoseconds(), such as microseconds() and
milliseconds(), which return the i64 values of the time that’s elapsed. Alternatively,
the seconds(), minutes(), and hours() methods return f64 values.
This feature is handy for measuring the time that’s elapsed while running a set of
instructions. For example, the following code demonstrates the time elapsed from
the moment the program starts until it finishes printing the value of the i variable for
module main
import time
fn main() {
sw := time.new_stopwatch()
for i in 1 .. 5 {
println('$i')
}
println('Total time took to finish:
$sw.elapsed().seconds() seconds')
}
go FUNCTION_NAME_1(OPTIONAL_ARGUMENTS)
h := go FUNCTION_NAME_1(OPTIONAL_ARGUMENTS)
h.wait()
h := go FUNCTION_NAME_1(OPTIONAL_ARGUMENTS)
h.wait()
The term channel indicates a medium or a path that allows you to transfer information or data from one end to the other. In the context of concurrency, channels allow us to share data by establishing a communication channel between the concurrent tasks. These concurrent tasks are often termed coroutines, which share the data by communicating through the channels. Channels are advanced concurrency patterns in V that solve the problem of explicitly handling data synchronization techniques among coroutines. We can communicate between the coroutines with the help of shared objects. In V, these can be structs, arrays, or maps. But the problem with this approach is that you need to take explicit care of concurrency synchronization techniques such as protecting the shared objects using locks such as the read-only rlock or the read/write lock to prevent data races, as we learned in the Sharing data between the main thread and concurrent tasks section of Chapter 10, Concurrency.
Channels in V can be compared to queues. Generally, a queue allows things or elements to pass through it in one direction. The first one to enter the queue is the first one to exit the queue. So, the values that are sent to the channel are accessed in a First In First Out (FIFO) manner.
CHANNEL_VARIABLE := chan DATA_TYPE{OPTIONAL_CAPACITY: CAPACITY_
VALUE}
In this syntax, the channel variable will be of the chan DATA_TYPE. OPTIONAL_CAPACITY type. This is a syntactical representation of the cap property that accepts an integer value. The cap property is available on a channel variable that represents the capacity of the values the channel could hold. The type could be any type, such as a primitive type, or it could be a struct, a map, or an array.
Having understood the basic syntax, we will learn about unbuffered channels and buffered channels in the following subsections.
A channel defined without capacity is referred to as an unbuffered channel. An unbuffered channel has a capacity of 0 by default. The following code shows how to define an unbuffered channel. This channel accepts integer values that are pushed onto it:
uc := chan int{}
println(uc.cap) // 0
println(typeof(uc).name) // chan int
Unlike unbuffered channels, a buffered channel is defined with a non-zero capacity, with its cap property assigned with an integer value. The following code shows how to define a buffered channel:
bc := chan string{cap: 2}
println(bc.cap)
println(typeof(bc).name)
The direction of the arrow operator, <-, indicates that the values always enter the channel from the right and exit the channel to the left.
ch := chan VALUE_TYPE {OPTIONAL_CAPACITY: CAPACITY_VALUE}
ch <- VALUE_TO_PUSH
ch := chan int{cap: 1}
ch <- 51
println(ch)
Place the preceding code in a file and run it using the v run filename.v command.
You should see that the console halts and doesn’t show any output. This means that the program entered the blocked state as soon as it encountered the push operation; that is, ch <- 51. We will understand why using push operations on an unbuffered channel
blocks the program’s execution in more detail in the Working with unbuffered channels section of this chapter.
ch := chan int{cap: 1}
ch <- 51
println('channel after push: $ch.str()')
println('popping value out of the channel and storing it in immutable variable x')
x := <-ch
println('value of x: $x')
println('channel after pop: $ch.str()')
You can obtain information about a channel variable by accessing the properties
it exposes. The properties of the channel include len, cap, and closed. These properties provide the following information about a channel at the time of accessing them:
- [] cap is an integer property that indicates the capacity of the channel. This is 0 for the unbuffered channel. In the case of a buffer ed channel, the cap property indicates the maximum number of values a channel can hold.
- [] len is an integer property that indicates the actual number of values that the channel holds at the time of accessing this property. At any given point in time, the len value can only be less than or equal to the cap property.
- [] closed is a Boolean property, and when its value is true, it indicates that the channel is closed. If a channel is not closed, the v alue of the closed property will be false.
Understanding channel properties using examples
b := chan string{cap: 2}
b <- 'hello'
println('capacity: $b.cap')
println('length: $b.len')
println('closed: $b.closed')
V exposes some public methods to control a channel’s behavior. These methods include the following:
• try_push()
• try_pop()
• close()
Except for the close() method, the try_push() and try_pop() methods have a return value, which is a built-in enum type called ChanState. The ChanState enum has three enum values:
• not_ready
• closed
• success
Performing try_push() or try_pop() on a channel can return one of the three aforementioned states. In this section, we will learn how to work with the try_push() Function with buffered and unbuffered channels and then we cover how to work with the try_pop() and close() methods.
Using try_push() on unbuffered channels
try_push() gracefully pushes data into the channel and returns the status in the form of an enum value from ChanState. The try_push() method accepts values of the type that the channel accepts. For an unbuffered channel, the try_push() operation returns a .not_ready value of ChanState enum, when there is no coroutine ready to pop a value out of the channel. To demonstrate this, consider the following code:
v := 'hi'
ch := chan string{} // unbuffered channel
res := ch.try_push(v)
println(res) // not_ready
Using try_push() on buffered channels
x := 'hello'
ch := chan string{cap: 2}
for {
status := ch.try_push(x)
if status == .success {
println('Channel length: $ch.len')
} else {
println('channel status: $status')
break
}
}
module main
fn main() {
ch := chan int{}
defer {
ch.close()
}
ch <- 3
x := <-ch
println(x)
println('End main')
}
os.base("/home/koosha/Desktop/File.exe")
Result: File.exe
os.chdir("/home/koosha/Desktop")!
os.getwd()
os.cp("test.v" , "/home/koosha/Desktop")!
cp_all will recursively copy src to dst,optionally overwriting files or dirs in dst
os.cp_all("/home/koosha/Desktop" , "/run/media/koosha/SSD/Desktop")!
create creates or opens a file at a specified location and returns a write-only File object.
os.create("/tmp/V/testFilecreate.v")!
execute starts the specified command, waits for it to complete, and returns its output.
os.execute("ls")
os.execute_or_exit("ls")
os.exists("/home/koosha/Desktop")
os.get_line()
os.input("Enter UserName : ")
os.input_password("password : ")
import os
fn main(){
username := os.input("username : ")
password := os.input_password("password : ")!
if username == "koosha" && password == "159753"{
println("Logined")
}else{
println("Login ERROR")
}
}
os.getpid()
os.geteuid()
os.id_dir("/home/koosha/Desktop")
os.id_dir_empty("/home/koosha/Desktop")
fn mkdir(path string, params MkdirParams) !
os.mkdir("/tmp/test")!
os.mv("/home/koosha/Desktop/File1.v" , "/home/koosha/Documents")!
os.open("/tmp/test.txt")!
open_append tries to open a file from a given path.
If successful, it and returns a File for appending.
os.open_append("/home/koosha/Downloads/Test.txt")!
os.readfile("test.txt")!
os.read_bytes("test.txt")
os.rename("test.txt" , "test.v")!
os.rename_dir("desktop" , "Desktop")!
os.rm("test.txt")
os.rmdir("/tmp/Test")!
os.rmdir_all("/Documents")!
system works like exec, but only returns a return code.
os.system("ls *.txt")
os.uname()
os.wait()
walk_ext returns a recursive list of all files in path ending with ext.
os.walk_ext("/home/koosha/Documents" , "md")
os.write_file("VLang.txt" , "write_file writes text data to the file in path.\nIf path exists, the contents of path will be overwritten with the contents of text.")
mport db.mysql
// Create connection
mut connection := mysql.Connection{
username: 'root'
dbname: 'mysql'
}
// Connect to server
connection.connect()?
// Change the default database
connection.select_db('db_users')?
// Do a query
get_users_query_result := connection.query('SELECT * FROM users')?
// Get the result as maps
for user in get_users_query_result.maps() {
// Access the name of user
println(user['name'])
}
// Free the query result
get_users_query_result.free()
// Close the connection if needed
connection.close()
fn connect(config Config) !DB
fn debug(debug string)
To optimize a Python script using Vlang, you would need to rewrite the performance-critical parts of your code in Vlang. V is a statically typed, compiled language that’s designed for performance, and it can be integrated with Python code where necessary. Here’s a general approach to optimize a Python script with Vlang:
- Identify Performance Bottlenecks:
- Use profiling tools (e.g., cProfile) to identify which parts of your Python code are consuming the most time.
- Focus on the most time-consuming functions or loops, as optimizing these will provide the most significant performance improvements.
- Rewrite Critical Code in Vlang:
- Install the Vlang compiler (https://vlang.io/).
- Rewrite the identified performance-critical functions or loops in Vlang. Vlang’s static typing and efficient memory management can lead to faster execution.
- Create Python Bindings:
- To call the Vlang code from Python, you’ll need to create Python bindings. You can use libraries like CFFI or ctypes to create these bindings.
- Load the Vlang shared library in Python and create Python wrappers for the Vlang functions.
- Test and Benchmark:
- Before deploying the optimized code, test it extensively to ensure correctness and measure its performance improvements. You can use Python’s timeit module or external benchmarking tools.
- Integration:
- Replace the original Python functions with your optimized Vlang functions where appropriate.
- Ensure that you handle data exchange between Python and Vlang correctly using the Python bindings you created.
- Profiling and Fine-Tuning:
- Profile the entire application again to ensure that your optimizations have improved the performance as expected.
- Fine-tune the Vlang code as needed to further enhance performance.
- Error Handling:
- Be sure to handle errors and exceptions correctly when calling Vlang code from Python to maintain application stability.
Here’s a simple example of using Vlang with Python:
Vlang code (example.v):
v
module example
fn add(a int, b int) int {
return a + b
}
Python code:
python
import ctypes
# Load the Vlang shared library
example = ctypes.CDLL('./example.so')
# Call the Vlang function from Python
result = example.add(5, 3)
print(result) # Should print 8
Remember that optimizing code with Vlang requires careful consideration and may not be necessary for all Python projects. It’s most effective for performance-critical parts of your application, and you should always measure and test your optimizations to ensure they provide the expected benefits.
Module List : https://vpm.url4e.com/
Getting Started with V Programming from Navule Pavan Kumar Rao
https://github.com/vlang/v
https://vpm.url4e.com/