V 语言中文文档



介绍

V是一种静态类型的编译编程语言,用于构建可维护的软件。
它与Go类似,也受到Oberon,Rust,Swift的影响。
V是一种非常简单的语言。通过这个文档将花费你大约半个小时,到最后你将学习几乎整个语言。
尽管很简单,但它为开发人员提供了很多动力。你可以用其他语言做任何事情,你可以用V做。(官方文档入口:https://vlang.io/docs#option)

Hello World

    //hello.v
    fn main() {
        println('hello world')
    }
  • 和 c语言一样,main 为程序入口函数
  • fn 声明函数,函数的返回值类型在 名称() 的后面,此处不返回任何内容所以忽略;
  • println 是为数不多的内置函数之一,作为标准输出;
  • fn main()函数也可以被忽略,方便在编写小程序应用中,或者脚本,或者学习语言时带来方便;这样代表’Hello World’可以这么写
    //hello.v
    println('hello world')

注释

    // 单行注释
    
    /* 
        这是多行注释
        /* 它可以嵌套 */
    */

函数 Functions

fn main() {
	println(add(77, 33))
	println(sub(100, 50))
}

fn add(x int, y int) int {
	return x + y
}

fn sub(x, y int) int {
	return x - y
}
  • 再次声明,返回值的类型在 名称的后面,
  • 和Go 和 C 一样,函数不能重载,这也简化了代码,提高了可维护性和可读性
  • 函数的使用可以在声明之前:例如上面, add 和 sub 在main 之后声明,也可以被 main 调用。对于 V 语言,所有声明都是如此,并且不需要头文件或考虑文件和声明的顺序。

变量

    name := 'Bob' 
    age := 20
    large_number := i64(9999999999)
    println(name)
    println(age)
    println(large_number) 
    
  • 变量的声明和初始化 使用 := ;这是V语言声明变量的唯一方式,这也意味着变量的初始化始终具有初始值。
  • 变量的类型是右侧的值推断出来的。要强制使用其他类型,请使用强制类型转换;表达式 T(v) 将值 v 转换为 T类型
  • 与大多数语言不同,V 只允许在函数中定义变量,不允许使用全局(模块级别)变量,没有全局状态。
    mut age := 20
    println(age)
    age = 21
    println(age)
  • 【mut】改变变量的值,使用 = 符号。在V语言中,默认情况下变量是不可改变的,为了得到一个允许被修改的变量,你必须在声明时,用 mut来修饰。
    • 您可以将上面例子的 mut 删除过后编译试试。
    • 请注意 := 和 =之间的区别;:=用于声明和初始化,=用于赋值
    • 请看如下3例错误示例
    fn main() {
    	age = 21 //这段代码在V语言中是编译不通过的,因为age未声明变量。
    }
    fn main() {
    	age := 21 //此段代码也不会被编译通过,因为未使用的变量会导致编译错误
    }
    fn main() {
    	a := 10 
    	if true {
    		a := 20 //与大多数语言不同,不允许使用已被声明的变量在局部变量。
    		//声明已在父作用域中使用的变量名,将导致编译错误。
    	} 
    }

基本类型

  • bool 布尔类型
  • string 字符串类型
  • i8 i16 i32 i64 i128 (soon)
  • u8 u16 u32 u64 u128 (soon)
  • byte // 是 u8 的别名
  • int // 是 i32 的别名,与C,Go不同,int 始终为 32字节
  • rune // 是 i32 的别名,表示Unicode代码点
  • f32 f64
  • byteptr
  • voidptr

字符串 Strings

   name := 'Bob' 
   println('Hello, $name!')  // `$` 用与字符串内引入变量
   println(name.len) //字符串长度
   
   bobby := name + 'by' // 通过 + 拼接字符串
   println(bobby) // ==> "Bobby"  
   
   println(bobby.substr(1, 3)) // 字符串截取 ==> "ob"  
   // println(bobby[1:3]) // 这种写法很可能会替换 substr 方法 
  • V语言中,字符串是只读字节数组,字符串数据使用UTF-8编码。
  • 单引号和双引号都可以用于表示字符串(TODO:暂时不支持双引号)。为了保持一致,vfmt 请将双引号转换为单引号,除非字符串包含单引号。
  • 字符串是不可变的,这代表 substring 函数非常有影响:不执行复制,不需要额外的分配。
  • V语言中,所有运算符必须在两侧都具有相同类型的值。如果有一个int 类型的变量 age:
    println('age = ' + age) //此段代码将无法编译通过,需改为如下
    
    println('age = ' + age.str()) //将 age 转换为 string ,或者使用
    
    println('age = $age') //字符串引入变量的方式

数组 Arrays

nums := [1, 2, 3]
println(nums)
println(nums[1]) // ==> "2" 

mut names := ['John']
names << 'Peter' //数组中添加元素
names << 'Sam' 
// names << 10  //这里将编译不通过,因为 names 是一个字符串数组
println(names.len) // ==> "3"  
println('Alex' in names) // ==> "false" 判断变量是否在数组中

// 我们也可以预先分配一定数量的元素
nr_ids := 50
mut ids := [0 ; nr_ids] // 这里将创建 包含50个0的数组
  • 数组的类型由第一个元素决定
    • [1,2,3] 是int数组 ([]int)。
    • [‘a’,‘b’] 是一个字符串数组 ([]string)
    • 所有元素必须相同类型,[1,‘a’] 将编译不通过
  • 数组的追加, << 将一个值附加到数组末尾的运算符
  • 数组长度,arr.len 字段。注意:len是一个只读字段,用户无法修改。
  • 是否包含:val in arr ;如果arr包含val返回 true。

Map

mut m := map[string]int{} // 现在 map的键值只能为 string 类型, 
m['one'] = 1 //设置值
println(m['one']) // ==> "1"  //获取值
println(m['bad_key']) // ==> "0"  //不包含返回 0 
println('bad_key' in m) // 使用in 关键字来判断是否存在 key

numbers := {
	'one': 1,
	'two': 2,
} 

IF 用法

if 语句非常简单,与大多数其他语言类似。

与其他类C语言不同,条件周围没有括号,并且始终需要大括号。

  • if 可以用作表达式(类似3位运算符):
num := 777
s := if num % 2 == 0 {
	'even'
}
else {
	'odd'
}
println(s) // ==> "odd"

运算符 in

  • in 用于检测 数组中是否包含 某元素
    nums := [1, 2, 3]
    println(1 in nums) // ==> true 
  • in 拓展用法,可以编写更清晰的布尔判断逻辑
//标准写法
if parser.token == .plus || parser.token == .minus || parser.token == .div || parser.token == .mult {
	... 
} 

//优化后写法
if parser.token in [.plus, .minus, .div, .mult] {
	... 
} 
// V 语言在这样的表达式中有做优化,上面两个语句会生成相同的机器码,不会创建任何数组

循环语句 FOR

V 语言只有一种循环方法:for

  • 数组的循环: 使用 for value in arr 的方式循环取数组中的元素;如果需要索引,可以使用 for index,value in arr的方式代替
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
}                             //         1) Peter

  • 数组循环时,value是只读的,如果要在循环时修改数组,必须使用索引:
mut numbers := [1, 2, 3, 4, 5]
for i, num in numbers {
	println(num)
	numbers[i] = 0
}
  • 类似while的循环方式实现:这种循环方式类似其他语言的 while 循环。一但条件不满足,循环停止,同样,条件周围不需要使用(),代码块总需要{}
mut sum := 0
mut i := 0
for i <= 100 {
	sum += i
	i++
}
println(sum) // ==> "5050" 

  • 循环的条件可以省去,这样循环会出现无限循环
    mut num := 0
    for {
    	num++
    	if num >= 10 {
    		break 
    	} 
    }
    println(num) // ==> "10" 

  • 传统的 for i 循环:同其他语言风格一样。他比 while形式更安全,因为后者很容易忘记更新计数器并陷入无限循环
    • 这里的 i 无需 mut 修饰,因为在这里 默认会被定义为 mut 类型的
    for i := 0; i < 10; i++ {
    	println(i)
    }

Match (原 Switch)

	//老版本V 使用
    os := 'windows' 
    print('V is running on ')
    switch os {
    case 'darwin':
    	println('macOS.')
    case 'linux':
    	println('Linux.')
    default:
    	println(os) 
    }
	//官方文档已经改为
	os := 'windows' 
	print('V is running on ')
	match os {
		'darwin' => println('macOS.')
		'linux' => println('Linux.')
		else => println(os) 
	}

匹配语句是编写if – else语句序列的较短方式。找到匹配的分支时,将分配=>后面的表达式。当没有其他分支匹配时,将分配else =>分支。

注意:与C不同,每个代码块的末尾都不需要 break。

Structs 结构体

  • 简单使用
    struct Point {
    	x int
    	y int 
    } 
    
    p := Point{
    	x: 10 
    	y: 20 
    } 
    println(p.x) // 访问结构体对象的某字段
  • 结构体的内存是在堆上分配,需要获取结构体指针,使用 &前缀:
    pointer := &Point{10, 10}  // 字段少于3的结构体的简化初始化方式
    println(pointer.x) // 指针访问结构体属性方式与默认的方式相同 
  • V 语言没有子类,但它支持嵌入式结构:
    // TODO: 这个功能将与7月末实施
    struct Button {
    	Widget
    	title string
    }
    
    button := new_button('Click me')
    button.set_pos(x, y)
    
    // 没有潜入式,我们需要这么做
    button.widget.set_pos(x,y)

结构体-访问修饰符

结构字段默认是私有的和不可变的(使结构也是不可变的)。
他们可以被 pub(可被访问的)和mut(可被修改的) 修饰。总共有5种可能的选择:

    struct Foo {
    	a int     // 私有的 不可变的 (default) 
    mut: 
    	b int     // 私有的 可变的
    	c int     // (你可以一次列出,相同访问修饰符的字段)   
    pub: 
    	d int     // 公开的,不可变的
    pub mut: 
    	e int     // 公开的,但仅在父模块中可变 
    pub mut mut: 
    	f int 	  // 公开的,父模块内部和外部都可变 
    }                 // (不建议使用,这就是为什么它如此冗长的原因) 
  • 例子,string 在 builtin 模块中定义的类型:
struct string {
	str byteptr
pub:
	len int
}

// 很容易看出 string 内容是不可变的
  • 错误示例:
fn main() {
	str := 'hello' 
	len := str.len // OK  
	str.len++      // 编译出错
}
//具有字符串数据的字节指针根本无法在外部builtin访问。 len字段是公开的,但不是可变的。

结构体-方法

V 没有 class概念,但是我们可以在结构体上定义方法。
方法是具有特殊行参的函数。特殊行参 放在 fn关键字 和方法名之间的参数列表中。

  • 简单示例(can_register 方法具有 User 类型的 特殊行参 u):
    • 不和其他语言一样,使用 self 或 this ,而是使用短名称,当然最好一个字母长
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"  

纯函数

V语言的函数默认是纯函数,也就是函数的输出结果只依赖输入的参数,并且没有其它的影响。
因为V语言没有全局变量,且所有的参数默认都是只读的,即使传入的引用也是默认只读的。
然后V语言并不纯的函数式语言。我们可以通过mut来修饰行参数,使得可以被修改:

struct User {
mut:
	is_registered bool 
} 

fn (u mut User) register() {
	u.is_registered = true 
} 

mut user := User{} 
println(user.is_registered) // ==> "false"  
user.register() 
println(user.is_registered) // ==> "true" 

  • 如上示例:特殊行参(这里被修饰了第一个参数) 被mut 修饰,为可变的。因此 register() 可以更改用户对象。
    • 当然也适用于函数的普通参数,示例如下:
fn multiply_by_2(arr mut []int) {
	for i := 0; i < arr.len; i++ {
		arr[i] *= 2
	}
}
mut nums := [1, 2, 3]
multiply_by_2(mut nums)
println(nums) // ==> "[2, 4, 6]"

注意1:您必须在执行函数之前 声明一个 mut 的nums 变量。这清楚的表明被调用的函数将要修改该值!

  • 最好使用返回值而不是修改参数。修改参数只应在应用程序的性能关键部分中完成,以减少分配和复制。
    • 使用user.register()或user = register(user) 代替register(mut user)
fn register(u User) User { 
	return { u | is_registered: true } 
}

user = register(user) 

常量 Constants

常量声明为const。它们只能在模块级别上(函数body外)定义。
常量名称必须大写。这有助于将它们与变量区分开来
永远不能改变常量值。

  • 简单使用
    
    const (
    	PI    = 3.14
    	World = '世界'
    ) 
    
    println(PI)
    println(World)

  • 复杂类型的常量定义(这相对大多数语言更灵活)
    struct Color {
            r int
            g int
            b int
    }
    
    fn (c Color) str() string { return '{$c.r, $c.g, $c.b}' } //重写 str方法
    
    fn rgb(r, g, b int) Color { return Color{r: r, g: g, b: b} }
    
    const (
            Numbers = [1, 2, 3]
    
            Red  = Color{r: 255, g: 0, b: 0} //结构体常量
            Blue = rgb(0, 0, 255)//结构体常量
    )
    
    println(Numbers)
    println(Red)
    println(Blue)

不支持使用全局变量,因此这种方式显得非常有必要

模块 Modules

V 语言是一个模块化的语言。鼓励创建可重用的模块,而且创建模块也非常简单。

  • 新模块创建,请创建一个包含你模块名的目录 和 .v 代码文件:
cd ~/code/modules
mkdir mymodule
vim mymodule/mymodule.v
// mymodule.v

module mymodule

// 要用 pub修饰符修饰,外部才能访问该 模块函数
pub fn say_hi() {
	println('hello from mymodule!')
}

  • 模块的编译:

    • 您可以在 你定义好的 模块目录下创建 更多的 .v模块文件
    • 构建命令 v -lib ~/code/modules/mymodule ,然后就可以在代码中使用它
  • 模块的使用:

    module main
    
    import mymodule
    
    fn main() {
    	mymodule.say_hi()
    }
请注意,每次调用外部函数时都必须指定模块。这看起来似乎很冗长,但它使代码更易读,更容易理解,因为它始终清楚的表达出从哪个模块调用哪个函数。特别是在大型代码库中。
模块名称应短,不超过10个字符。模块不允许循环依赖。
您可以在任何地方创建模块 ,所有的模块都将静态编译到单一的可执行程序中。

接口 Interfaces

  • 简单使用:

    类型通过实现接口同名的方法(注意返回值也相同类型)。和Go语言一样,V语言也是隐式接口,类型不需要显式实现接口,没有“implements”关键字。

    struct Dog {}
    struct Cat {}
    
    fn (d Dog) speak() string { 
    	return 'woof'
    } 
    
    fn (c Cat) speak() string { 
    	return 'meow' 
    } 
    
    interface Speaker {
    	speak() string
    }
    
    fn perform(s Speaker) { 
    	println(s.speak())
    } 
    
    dog := Dog{} 
    cat := Cat{} 
    perform(dog) // ==> "woof" 
    perform(cat) // ==> "meow" 
    

枚举

enum Color {
	red green blue 
} 

mut color := Color.red
// 下面的写法要注意了,V语言知道 color的类型为Color,所以这个地方不需要使用 Color.green
color = .green 
println(color) // ==> "1"  TODO: print "green"?  

可选类型 和 错误处理

V语言针对函数返回值增加了一个可选的属性,这样可以用于处理失败的情况

  • 可以用最小工作量来升级一个可选类型返回的函数:只需要在 返回值类型声明的前面添加一个 ?,这样就可以区别错误和真正的返回值。
    • 当然如果你不需要返回错误,可以简单的使用 return None;(TODO:None尚未实现)

这是处理V中错误的主要手段。函数的返回值依然是值,但是错误处理要简洁很多。

struct User {
	id int 
	name string
} 

struct Repo {
	users []User 
} 

fn new_repo() Repo {
	return Repo {
		users: [User{1, 'Andrew'}, User {2, 'Bob'}, User {10, 'Charles'}]
	}
} 

fn (r Repo) find_user_by_id(id int) ?User { 
	for user in r.users {
		if user.id == id {
			// V 自动将其包装为 Option 类型  
			return user 
		} 
	} 
	return error('User $id not found') 
} 

fn main() {
	repo := new_repo() 
	user := repo.find_user_by_id(10) or { // Option类型必须使用 `or` 的代码块hold住;
		return  // `or` 代码块必须以, return ,break,或 continue 关键字结束 
	} 
	println(user.id) // ==> "10"  
	println(user.name) // ==> 'Charles'
}
  • 当然,错误还可以继续传播:
resp := http.get(url)?
println(resp.body)

上面例子中,http.get return ?http.Response 的可选类型,如果错误发生,将传播到调用函数,这里是导致main函数抛出异常。
上面代码是下面代码的简写:

resp := http.get(url) or {
	panic(err)
}
println(resp.body)

范型 Generics (预计7月上线)

为了方便阅读, 允许使用 ⟨⟩ 来代替 <>. vfmt 自动替换 ⟨⟩ 为<> .

struct Repo⟨T⟩ {
	db DB
}

fn new_repo⟨T⟩(db DB) Repo⟨T⟩ {
	return Repo⟨T⟩{db: db}
}

// 这是一个范型函数. V 语言可以使用任意类型的范型  
fn (r Repo⟨T⟩) find_by_id(id int) ?T {  
	table_name := T.name // 在此示例中,获取类型的名称会得到表明 
	return r.db.query_one⟨T⟩('select * from $table_name where id = ?', id)
}

db := new_db()
users_repo := new_repo⟨User⟩(db)
posts_repo := new_repo⟨Post⟩(db)
user := users_repo.find_by_id(1)? 
post := posts_repo.find_by_id(1)? 

并发

并发模型与Go非常相似。要foo()同时运行,只需调用它go foo(),便会在新的系统线程执行该函数 .
很快 将实现 goroutines 和 scheduler 。

JSON 解析

JSON 在今天已经非常流行,这也是为什么要内置json支持的原因。

  • json.decode 函数的第一个参数是要解码的类型,第二个参数是 json字符串。
  • json 用于生成json 编码和解码的代码,不使用运行时反射,这也会带来更好的性能。
struct User {
	name string
	age  int 
	foo  Foo    [skip]  //使用 skip 来跳过某些字段
} 

data := '{ "name": "Frodo", "age": 25 }'
user := json.decode(User, data) or {
	eprintln('Failed to decode json')
	return 
} 
println(user.name)
println(user.age) 

单元测试

所有的单元测试都必须放在 *test.v 文件中,并且测试函数必须使用 test 开头方式命名。

  • 若要运行 测试文件 hello_test.v 用来测试某整个模块,请执行命令 v test mymodule
// hello.v 
fn hello() string {
	return 'Hello world'
} 

// hello_test.v 
fn test_hello() {
    assert hello() == 'Hello world'
} 

内存管理

V语言没有自动内存回收(GC)和引用计数,V语言会在编译阶段完成必要的清理工作。例如:

fn draw_text(s string, x, y int) {
	...
}

fn draw_scene() {
	... 
	draw_text('hello $name1', 10, 10)
	draw_text('hello $name2', 100, 10)
	draw_text(strings.repeat('X', 10000), 10, 50)
	... 
}

字符串 string 生命周期不会超出 draw_text 函数,因此当函数执行完时,它们将会被清除。实际上,前面两次调用根本不会导致任何分配,因为这两个字符串很小,V语言会使用提前准备好的缓冲区构造字符串。

  • 对于更复杂的情况,需要手动内存管理,我们将很快实现解决。

  • V 将在运行时检测并报告内存泄露,例如:要清除数组,请使用 free() 方法:

   numbers := [0; 1000000] 
   ...
   numbers.free() 

延迟 Defer

延迟代码块将推迟执行,直到所属的函数执行结束或return。

fn read_log() {
	f := os.open('log.txt')
	defer { f.close() }
	...
	if !ok {
		// 将在此处调用defer语句,文件将被关闭
		return
	}
	...
	//将在此处调用defer语句,文件将被关闭
}

vfmt 代码格式化

您不需要担心V语言的代码格式化问题, vfmt 帮你解决:

	v fmt file.v

建议设置编辑器,以便vfmt在每次保存时运行,始终允许vfmt.

在推送代码之前始终运行vfmt。

进阶内容

V语言中调用C 函数

#flag -lsqlite3

#include "sqlite3.h"

struct C.sqlite3 
struct C.sqlite3_stmt 

fn C.sqlite3_column_int(C.sqlite_stmt, int) int 

fn main() {
	path := 'sqlite3_users.db' 
	db := &C.sqlite3{} 
	C.sqlite3_open(path.cstr(), &db)

	query := 'select count(*) from users' 
	stmt := &C.sqlite3_stmt{} 
	C.sqlite3_prepare_v2(db, query.cstr(), - 1, &stmt, 0)
	C.sqlite3_step(stmt) 
	nr_users := C.sqlite3_column_int(res, 0)
	C.sqlite3_finalize(res)
	println(nr_users) 
} 

  • C strings 可以转换为 “string(cstring)”的V字符串

预编译if语句

预编译 使用 $if 。现在它只能用于检测操作系统。

$if windows {
	println('Windows')  
}  
$if linux {
	println('Linux') 
} 
$if mac {
	println('macOS') 
}

代码反射

有内置的 json支持已经是很不错的选择,但是V还允许使用者为任意添加高效的序列化程序。

// TODO: 计划7月上线
fn decode(data string) T {
        mut result := T{}
        for field in T.fields {
                if field.typ == 'string' {
                        result.$field = get_string(data, field.name)
                } else if field.typ == 'int' {
                        result.$field = get_int(data, field.name)
                }
        }
        return result
}

// 生成:
fn decode_User(data string) User {
        mut result := User{}
        result.name = get_string(data, 'name')
        result.age = get_int(data, 'age')
        return result
}

有限的运算符重载

运算符重载违背了V的简单性和可预测性的理念。但由于科学和图形应用程序属于V域,因此为了提高可读性,运算符重载非常重要:

a.add(b).add(c.mul(d))比 a + b + c * d 可读性差得多了。

struct Vec {
	x int
	y int
}

fn (a Vec) str() string { 
	return '{$a.x, $a.y}' 
}

fn (a Vec) + (b Vec) Vec {
	return Vec {
		a.x + b.x, 
		a.y + b.y 
	}
}

fn (a Vec) - (b Vec) Vec {
	return Vec {
		a.x - b.x, 
		a.y - b.y
	}
}

fn main() { 
	a := Vec{2, 3}
	b := Vec{4, 5}
	println(a + b) // ==> "{6, 8}"  
	println(a - b) // ==> "{-2, -2}"  
} 
  • 为了提高安全性和可维护性,运算符重载 有几个局限性:
    • 只能使用 +,=,* ,/ 运算符的重载
    • 不允许运算符重载函数内调用其他函数
    • 运算符重载函数无法修改其参数
    • 运算符函数的两个参数必须具有相同类型,(同V中所有运算符一样,str+num => error)

内联汇编

TODO 尚未实现

fn main() {
	a := 10
	asm x64 {
		mov eax, [a]
		add eax, 10
		mov [a], eax
	}
}

将 C/C++翻译成V

TODO: 将C翻译为V将于7月上市。C ++到V将于今年晚些时候推出

V可以将您的C / C ++代码转换为可读的V代码。让我们先创建一个简单的程序test.cpp

#include 
#include 
#include 

int main() {
        std::vector s;
        s.push_back("V is ");
        s.push_back("awesome");
        std::cout << s.size() << std::endl;
        return 0;
}

允许命令 v translate test.cpp 将会得到 test.v:

fn main {
        mut s := []
	s << 'V is '
	s << 'awesome'
	println(s.len) 
} 

一个在线C / C ++到V的翻译即将推出.

  • 什么时候应该翻译C代码,什么时候应该从V调用C代码?
    • 如果您有经过良好编写且经过良好测试的C代码,那么您当然可以直接从V调用此C代码
    • 将其翻译为V有以下几个优点:
      • 如果您计划开发该代码库,那么现在您可以使用一种语言编写所有内容,这样可以更安全,更容易开发
      • 交叉编译(mac 编译打包 win程序)变得更加容易。您根本不必担心它。
      • 没有更多的构建标志和包含文件。

热门代码重加载

module main

import time
import os

[live]
fn print_message() {
	println('Hello! Modify this message while the program is running.')
}

fn main() {
	for {
		print_message()
		time.sleep_ms(500)
	}
}

构建此示例:v -live message.v
要重新加载的函数必须 将 [live] 在定义之前添加。
现在,在程序运行时无法修改类型。

更多示例,包括图形应用程序: github.com/vlang/v/tree/master/examples/hot_code_reloading。

交叉编译 (mac 编译打包 win程序)

  • 要交叉编译项目,只需简单执行命令:

    • v -os windows .
    • v -os linux .

    (暂时无法对macOS进行交叉编译。)

  • 如果您没有任何C依赖项,那就是您需要做的。即使在使用ui模块或图形应用程序编译GUI应用程序时也可以使用 gg。

  • 您需要安装Clang,LLD链接器,并下载包含库和包含Windows和Linux文件的zip文件。V将为您提供链接。

V中的跨平台shell脚本 (6月下旬)

  • V可以用作Bash的替代方案来编写部署脚本,构建脚本等。

  • 使用V的优势在于语言的简单性和可预测性以及跨平台支持。“V脚本”在类Unix系统和Windows上运行。

  • 使用#v指令启动程序。它将使os 模块中的所有函数全局化(您可以使用ls() 来代替os.ls() )例如:

    #v 
    
    rm('build/*') 
    // 效果同:  
    for file in ls('build/') {
    	rm(file) 
    } 
    
    mv('*.v', 'build/') 
    // 效果同:  
    for file in ls('.') {
    	if file.ends_with('.v') { 
    		mv(file, 'build/') 
    	} 
    } 

现在您可以像普通的V程序一样编译它,并获得可以在任何地方部署和运行的可执行文件: v deploy.v && ./deploy
(当然也可以使用传统脚本运行方式来允许它: v run deploy.v)

附录1:关键字

V 拥有22个关键字

break 
const  
continue 
defer 
else 
enum 
fn
for
go
goto
if
import
in 
interface 
match 
module 
mut 
or 
pub 
return
struct
type 

附录2:运算符

  • 运算符

    运算符 含义 支持类型
    + sum integers, floats, strings
    difference integers, floats
    * product integers, floats
    / quotient integers, floats
    % remainder integers
    & bitwise AND integers
    bitwise OR integers, floats
    ^ bitwise XOR integers
    << left shift integer << unsigned integer
    >> right shift integer >> unsigned integer
  • 运算符优先级

优先级 运算符
5 * / % << >> &
4 + – | ^
3 == != < <= > >=
2 &&
1 ||
  • 赋值运算符

    +=   -=   *=   /=   %=   &=   |=   ^=   >>=   <<=