编程语言之V
2021-09-11 20:47:46 阿炯

V语言是一个融合了Go语言的简洁性以及Rust的安全特性的全新语言,主要用于系统应用编程开发。其特性在于快速编译、安全,并且可以和此前已有的C/C++转换。2019年6月,V语言开发者宣布开源,其首个可用版本发布。全新编程V语言带着其迷你编译器,无第三方依赖,采用V语言编写并在MIT协议下授权。


Simple, fast, safe, compiled. For developing maintainable software.

值得一提的是其编译器只有几MB且无第三方依赖,目前来看还并不完善,尤其是内存管理也面临着与Go和Rust同样繁琐的生命期管理问题,但相比于C++等手动和半自动的管理方式省心一些。开发者可以前往vlang.io网站得到源码,同时也可通过GitHub获取V语言源码。它被描述为一种简单,快速,编译的语言,用于创建可维护的软件。它的创造者Alex Medvednikov表示它与Go非常相似,并受到Oberon,Rust和Swift的启发。如果熟悉 Go,那就已经掌握了 80%。V 推崇以最小的抽象来写简单干净的代码,除了简单外,V 也能给予开发者很大的能力,所有能用其它语言实现的都能用 V 实现。


从V编程语言中可以期待什么

快速编译:V每秒可以编译多达120万行代码。它通过直接的机器代码生成和强大的模块化实现了这一点。如果决定发出C代码,编译速度将下降到每CPU每秒大约100k的代码。Medvednikov提到,直接生成机器码仍处于早期的阶段,目前只支持x64/Mach-O。

安全:它似乎是一种理想的语言,因为它没有null、全局变量、未定义的值、未定义的行为、变量阴影以及绑定检查。默认情况下,它支持不可变变量、纯函数和不可变结构。

性能:根据该网站介绍,V与C一样快,需要最少量的分配,并且支持内置序列化而无需运行时反射。它编译为原生二进制文件而没有任何依赖。与Go,Rust,GCC和Clang相比,V所需的空间和构建时间非常少。整个语言和标准库只有400 KB,您可以在0.4秒内构建它。到今年年底,作者的目标是将这个构建时间缩短到0.15秒。

C/C++转换:V允许将V代码转换为C或C++,但考虑到C和C++是一种非常复杂的语言,这个功能还处于初期阶段。创作者的目标是在当年年底之前使这个功能稳定下来。

特性:

快速编译(V每CPU核心每秒编译约120万行代码。也可以调用C,编译速度会下降到≈100k行/秒/CPU)。
没有:空指针、全局变量(意味着变量都是在函数体中声明)、未定义的行为、变量跟踪
边界检查、可选的结果类型、支持泛型、边界检测
默认不可变的变量、纯函数、不可变的结构体、默认使用 Immutable 结构体
没有:Null、全局变量、未定义的值
方便使用的交叉编译、提供跨平台 UI 库
内置图形库、ORM、Web 框架


性能
和C一样快,操作C没有任何成本
没有运行时反射的内置序列化
最少量的分配
可以编译程序到没有任何依赖关系的本地二进制文件:一个简单的web服务器只有65 KB

用于构建可维护程序的简单语

可以在半小时内浏览文档来学习整个语言(目前提供的标准库还不多,毕竟刚发布)。尽管很简单,但是它为开发人员提供了很多功能。任何能用其他语言做的事情都可以用V来做(这句话,貌似每个语言都会这么讲....)。整个语言及其标准库都小于400kb,V已经是用V语言完成的(完成了自举),可在数秒内完成。

热代码重载
立即获得更改,而无需重新编译。也不必在每次编译之后都进入正在处理的状态,这可以节省大量宝贵的开发时间。

强大的图形库
构建在GDI+/Cocoa绘图之上的跨平台绘图库,以及一个基于OpenGL的图形库,用于更复杂的2D/3D应用程序,它还具有以下特性:
加载复杂的三维对象与纹理在制品
摄像头(移动,环顾四周)wip
计划使用DirectX、Vulkan和Metal支持(图形库的一个简单例子是tetris.v(就是俄罗斯方块))。

本机跨平台GUI库
容易交叉编译
交叉编译您的软件只需运行v -os windows或者v -os linux。不需要额外的步骤,即使是GUI和图形应用程序(目前只在macOS上编译macOS软件)

无痛部署和依赖关系管理
大致特性就是这些,不过等这些功能完善,还是需要不少时间,这些特性也可以说是V以后的目标。V语言还比较新,功能完善和生态发展等都还需要时间。

V语言目前共有以下23个关键词:
break、const、continue、defer、else、enum、fn、for、go、goto、if、import、in、interface、match、module、mut、none、or、pub、return、struct、type

与其他语言的差异

与 C++ 相比,V 要简单得多。它提供高达 400 倍的编译速度,安全性强,缺少未定义的行为,易于并发,编译时间代码生成等。

与 Python 相比,V 更快,更简单,更安全,更易于维护。

V 的域与 Rust 类似,Rust 是一种复杂的语言,具有越来越多的功能和陡峭的学习曲线。V 的目标是允许构建可维护且可预测的软件,这就是 V 语言为何如此简单。它的好处在于可以跳进项目中的任何部分,了解正在发生的事情,它只有一种做事方式。此外,Rust 的编译速度很慢,与 C++ 相当,而 V 每秒每个 CPU 可编译 120 万行代码。

Fast compilationD, Go, Delphi
Simplicity & maintainabilityGo
Great performance on par with C and
zero cost C interop
C, C++, D, Delphi, Rust
Safety (immutability, no null, option types, free from data races)Rust
Easy concurrencyGo
Easy cross compilationGo
Compile time code generationD
Small compiler with zero dependencies-
No global state-
Hot code reloadingC# (.NET 6+), Dart

官网显示,V 语言支持 Windows,macOS,Linux,*BSD 系统,晚些时候在 Android 和 iOS 上也可以投入使用。开发者 Alex Medvednikov 表示在 V1.0 发布前,V 语言不会有太大的变化,除了可能会添加一些新功能,如 goroutines。

Go

V与Go很相似,但提高了如下部分:
1)没有全局状态 - No global state
2)没有NULL - No null
3)没有未定义值 - No undefined values
4)没有err - No err != nil checks (replaced by option types)
5)默认是不可变的 - Immutability by default
6)只有一种声明方式 - Only one declaration style (a := 0)
7)更小的运行时 - Much smaller runtime
8)更小的二进制包 - Much smaller binaries (a simple web server written in V is 65 KB vs 7 MB in Go)
9)零成本与C语言互操作 - Zero cost C interop
10)没有GC - No GC
11)更快的序列化,没有反射 - Much faster serialization using codegen and no reflection
12)预编译HTML模板,而不需要每次请求都要重新编译 - Precompiled HTML templates unlike html/templates that have to be parsed on every request
13)无畏并发- Fearless concurrency (no data race guarantee at compilation)
14)枚举 - Enums
15)泛型 - Generics
16)String插值 - String interpolation: println('$foo: $bar.baz')
17)更严格的vfmt保证一个编码风格 - Stricter vfmt to ensure one coding style
18)中心包管理器 - Centralised package manager

Rust

Rust有一个非常不同的哲学。它是一门复杂的语言,具有越来越多的特性和陡峭的学习曲线;但一旦学习并理解了该语言,它将成为开发安全、快速和稳定的软件的强大的工具。但复杂性仍然存在。V的目标是允许构建可维护和可预测的软件。这就是为什么这种语言如此简单,甚至对一些人来说很无聊。好的方面是,你可以投入到项目的任何部分中,并理解正在发生的事情,就像是你写的一样,因为语言很简单,而且只有一种方法可以做事情。Rust的编译速度很慢,与C++相当。

V语言中文基础教程

转自光谷码农的微信空间,感谢原作者。

Hello World

fn main() {
    println('hello V语言中文网:https://vlang-zh.cn')
}

函数用fn关键字定义或声明,返回类型在函数名称后面。在这个例子中main函数没有返回值,因此返回值类型被忽略了。和C语言一样,main函数是程序的入口函数。println是内置函数之一,它打印到标准输出。

在一个单一文件的V程序中,main函数可以被忽略,这对于学习语言的一些小代码片段很友好。为了演示,后面的例子就忽略了main函数。

因此“Hello World”程序可以写的再简单一点:
println('hello V语言中文网:https://vlang-zh.cn')

注释

// 单行注释
/* 多行注释.
   /* 支持嵌套注释. */
*/

函数

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 := 'FreeOA'
age := 20
large_number := i64(9999999999)
println(name)
println(age)
println(large_number)

变量使用:=声明和初始化,这是V语言唯一定义变量的方式。因此V语言中所有的变量必须指定一个初始化的值。

变量的类型是从右值推导来的。要使用其它类型,必须手工强制转换类型:使用T(v)表达式将v转换为T类型。

和其它主流语言不同的是,V语言只能在函数内部定义变量。V语言没有模块级别的全局变量,因此也没有全局状态。

mut age := 20
println(age)
age = 21
println(age)

使用=给变量重新赋值。不过在V语言中,变量默认是不可再次改变的。如果需要再次改变变量的值,必须用mut修饰变量。可以尝试删除mut,然后再编译上面的代码。

需要注意:=和=的差异,前者是用于声明和初始化变量,后者是重新给变量赋值。

fn main() {
    age = 21
}

上面的代码将不能编译,因为变量没有被声明过。V语言中所有的变量必须要先声明。

fn main() {
    age := 21
}

上面的代码依然不能被编译,因为V语言中禁止声明没有被使用的变量。

fn main() {
    a := 10
    if true {
        a := 20
    }
}

和很多其它语言不同的是,不同块作用域的变量不得重名。上面的例子中,变量a已经在外层被声明过,因此不能再声明a名字的变量。

基础类型

bool

string

i8  i16  i32  i64 i128 (soon)
u8  u16  u32  u64 u128 (soon)

byte // alias for u8
int  // alias for i32
rune // alias for i32, represents a Unicode code point

f32 f64

byteptr
voidptr

和C语言、Go语言不同的是,int始终是32bit大小。

字符串

name := 'Bob'
println('Hello, $name!')  // `$` is used for string interpolation
println(name.len)

bobby := name + 'by' // + is used to concatenate strings
println(bobby) // ==> "Bobby"  

println(bobby.substr(1, 3)) // ==> "ob"  
// println(bobby[1:3]) // This syntax will most likely replace the substr() method   

V语言中,字符串是一个只读的字节数组。字符串数据采用UTF8编码。

单引号和双引号都可以用户包含字符串面值(TODO:双引号目前还不支持)。为保持一致性,vfmt会将双引号字符串转换为单引号,除非该字符串包含单引号字符。

因为字符串是只读的,因此字符串的取子字符串的操作会比较高效:不需要复制,也不需要额外分配内存。

V语言中运算符两边值的类型必须是一样的。比如下面的代码,如果age是int类型的话,是不能正确编译的:
println('age = ' + age)

我们需要将age转换为string类型:
println('age = ' + age.str())

或者在字符串内部直接嵌入表达式(这是比较完美的方式):
println('age = $age')

数组

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

mut names := ['John']
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"

// We can also preallocate a certain amount of elements.
nr_ids := 50
mut ids := [0 ; nr_ids] // This creates an array with 50 zeroes

数组的第一个元素决定来数组的类型,比如[1, 2, 3]对应整数类型的数组[]int。而['a', 'b']对应字符串数组[]string。

数组中的每个元素必须有相同的类型,比如[1, 'a']将不能编译。

<<运算符用于向数组的末尾添加元素。

而数组的.len成员返回数组元素的个数。这是一个只读的属性,用户不能修改。V语言中所有导出的成员默认都是只读的。

val in array表达式判断val值是否是在数组中。

Maps

mut m := map[string]int{} // Only maps with string keys are allowed for now  
m['one'] = 1
println(m['one']) // ==> "1"  
println(m['bad_key']) // ==> "0"  
// TODO: implement a way to check if the key exists

numbers := { // TODO: this syntax is not implemented yet  
    'one': 1,
    'two': 2,
}

If

a := 10
b := 20
if a < b {
    println('$a < $b')
} else if a > b {
    println('$a > $b')
} else {
    println('$a == $b')
}

if语句和大多数编程语言类似。和C语言不同的是,条件部分不需要小括弧,而大括弧是必须的。

if同时也可以当作表达式使用:
num := 777
s := if num % 2 == 0{
    'even'
}else{
    'odd'
}
println(s) // ==> "odd"

`in`运算符

in运算符判断数组是否包含某个元素。

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

对于需多个值之一的相等判断比较简洁:
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一种循环结构。

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
}

其中for .. in循环用于迭代遍历数组中每个元素的值。如果同时还需要元素对应的索引的话,可以用for index, value in语法。

mut sum := 0
mut i := 0
for i <= 100 {
    sum += i
    i++
}
println(sum) // ==> "5050"

这种风格的循环和其它语言中的while循环类似。当循环条件为false的时候结束循环迭代。同样地,循环条件不需要小括弧,而大括弧又是必须的。

mut num := 0
for {
    num++
    if num >= 10{
        break
    }
}
println(num) // ==> "10"

循环的条件可以省略,省略后类似一个无限循环:
for i := 0; i < 10; i++ {
    println(i)
}

最后是C语言风格的for循环。这种方式的循环比while循环更安全,因为while循环很容易忘记更新循环的计数器。

这里的i不需要用mut声明,因为这里的变量默认是可变的。

`switch`多分支
impot os
os := 'windows'
print('V is running on ')
match os {
case 'darwin':
    println('macOS.')
case 'linux':
    println('Linux.')
default:
    println(os)
}
// TODO: replace with match expressions

switch对应多个if - else分支的简化。当遇到相等的第一个case对应的语句执行相应的语句。和C语言不同的是,不需要在每个case写break。

注意:新的版本已经用Match替换了switch

结构体

struct Point {
    x int
    y int
}

p := Point{
    x: 10
    y: 20
}
println(p.x) // Struct fields are accessed using a dot

上面的结构体都在栈上分配。如果需要在堆上分布,需要用取地址的&操作符:
pointer := &Point{10, 10}  // Alternative initialization syntax for structs with 3 fields or fewer
println(pointer.x) // Pointers have the same syntax for accessing fields

V语言不支持子类继承,但是可以嵌入匿名结构体成员:
// TODO: this will be implemented later in June
struct Button {
    Widget
    title string
}

button := new_button('Click me')
button.set_pos(x, y)

// Without embedding we'd have to do
button.widget.set_pos(x,y)

结构体成员访问修饰符

结构体成员默认是私有并且不可修改的(结构体模式是只读)。但是可以通过pub设置为公开的,通过mut设置为可写的。总体来说有以下五种组合类型:

struct Foo {
    a int     // private immutable (default)
mut:
    b int     // private mutable
    c int     // (you can list multiple fields with the same access modifier)   
pub:
    d int     // public immmutable (readonly)
pub mut:
    e int     // public, but mutable only in parent module  
pub mut mut:
    f int     // public and mutable both inside and outside parent module  
}                 // (not recommended to use, that's why it's so verbose)

例如在builtin模块定义的字符串类型:
struct string {
    str byteptr
pub:
    len int
}

可以看出字符串是一个只读类型。

字符串结构体中的byte指针在builtin模块之外不可访问,而len成员是模块外部可见的,但是外部是只读的。

fn main() {
    str := 'hello'
    len := str.len // OK
    str.len++ // Compilation error
}

方法

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语言没有类,但是可以基于类型定义方法。

方法是一种带有接收者参数的特殊函数。

接收者参数出现在fn关键字和方法名字之间,方法名之后也可以有普通的参数。

在上面的例子中,can_register方法有一个User类型的接收者参数u。V语言的习惯是不要用self或this这类名字作为接收者参数名,而是使用短小有意义的名字。

默认都是纯函数

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"  

在这个例子中,接收者参数u用mut关键字标注为可变的,因此方法内部可以修改user状态。mut也可以用于其它的普通参数:
fn multiply_by_2(mut arr []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]"

注意,调用函数的时候也必须给nums增加mut关键字。这样可以清楚表达被调用的函数可能要修改这个值。

最好是通过返回值返回结果,而不是修改输入的函数参数。修改参数尽量控制在程序性能比较关键的部分,这样可以即使那分配和复制的开销。

使用user.register()或user = register(user)代替 register(mut user)。

V语言可以用简洁的语法返回修改的对象:
fn register(u User) User {
    return { u | is_registered: true }
}

user = register(user)

常量

const (
    PI = 3.14
    Foa = 'http://www.freeoa.net'
    Con='我只是一段很变通的中文而已'
)

println(PI)
println(Foa)
println(Con)

常量通过const关键字定义,只能在模块级别定义常量,不能在函数内部定义常量。

常量名必须大写字母开头,这样有助于区别常量和变量(在新版本中似乎已经没有这一项规矩,甚至相反)。

常量值永远不会被改变。

V语言的常量支持多种类型,甚至是复杂类型的值:
struct Color {
    r int
    g int
    b int
}

fn (c Color) str() string { return '{$c.r, $c.g, $c.b}' }

fn rgb(r int, g int, 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)

因为不支持全局的变量,所以支持全局的复杂类型的常量就变得很有必要。

模块

V是一个模块化的语言。它鼓励创建可复用的模块,而且创建模块也很简单。要创建模块需要先创建一个同名的目录,然后里面包含.v后缀名的文件:
cd ~/code/modules
mkdir mymodule
vim mymodule/mymodule.v

// mymodule.v
module mymodule

// To export a function we have to use `pub`
pub fn say_hi() {
    println('hello from https://vlang-zh.cn!')
}

在mymodule目录下可以有多个v源代码文件。

然后通过v -lib ~/code/modules/mymodule命令编译模块。

然后就可以在自己的代码中使用了:
module main
import mymodule

fn main() {
    mymodule.say_hi()
}

每次调用模块中的函数必须在函数前面指定模块名。这虽然有点冗长,但是代码更容易阅读和为何,我们一眼就可以看出函数是属于那个模块的。在大型代码库中这很重要。

模块名要短小,一般不要超出10个字符,而且模块也不能出现循环依赖。

所有的模块都将静态编译到单一的可执行程序中。

接口

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"

类型通过实现的方法满足接口。和Go语言一样,V语言也是隐式接口,类型不需要显式实现接口。

枚举

enum Color {
    red green blue
}

mut color := Color.red
// V knows that `color` is a `Color`. No need to use `Color.green` here.
color = .green
println(color) // ==> "1"  TODO: print "green"?

可选类型和错误处理

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 automatically wraps this into an option type  
            return user
        }
    }
    return error('User $id not found')
}

fn main() {
    repo := new_repo()
    user := repo.find_user_by_id(10) or { // Option types must be handled by `or` blocks  
        return  // `or` block must end with `return`, `break`, or `continue`  
    }
    println(user.id) // ==> "10"  
    println(user.name) // ==> 'Charles'
}

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

将函数升级到可选类型的返回值很简单,只需要给返回值类型增加一个?就可以,这样就可以区别错误和真正的返回值。

如果不需要返回错误信息,可以简单返回Node(TODO:还没有实现)。

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

当然,错误还可以继续传播:
resp := http.get(url)?
println(resp.body)

http.get返回的是?http.Response可选类型。如果错误发生,将传播到调用函数,这里是导致main函数抛出异常。

上面代码是下面代码的简写:
resp := http.get(url) or {
    panic(err)
}
println(resp.body)

泛型

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()
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)?

为了方便阅读,允许使用⟨⟩代替<>。vfmt最终会将⟨⟩替换为<>。

并发

并发模型和Go语言类似。通过'go foo()'来并发执行foo()函数调用。目录每个并发函数运行在独立的系统线程。稍后会实现和goroutine类似的调度器。

JSON解码

struct User {
    name string
    age  int
    foo  Foo    [skip]  // Use `skip` attribute to skip certain fields
}

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

JSON是目前流行的格式,因此V语言内置了JSON的支持。

json.decode解码函数的第一个参数表示要解码的类型,第二个参数是JSON字符串。

V语言会重新生成JSON的编码和解码的代码。因为不使用运行时的反射机制,因此编码和解码的速度都非常快。

单元测试

// hello.v
fn hello() string {
    return 'Hello world'
}

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

所有测试函数都必须放在*_test.v文件中,测试函数以test_开头。通过v hello_test.v运行单个测试代码,通过v test mymodule测试整个模块。

内存管理

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

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)
    ...
}

因为字符串没有从draw_text函数逃逸,因此函数调用返回之后就可以被清理。实际上这几个函数调用不会产生任何内存分配的行为。因为两个字符串比较小,V语言会使用提前准备好的缓冲区构造字符串。

对于复杂的情况,目前还需要手工管理内存。但是我们将很快解决这个问题。

V语言运行时会检测内存泄露并报告结果。要释放数组,可以使用free()方法:

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


最新版本:0.2.4
于2021年8月末发布,更新内容众多,现节取部分内容如下:
通过 -autofree 命令实现编译时的内存管理。添加该命令后,编译出的程序占用内存会大大降低(比如打开一个较大的文件),该特性将在0.3版本中默认启用。
管道和锁机制(这点和golang很像)。
通过关键字 shared 可以声明线程安全类型数组。
支持结构嵌入与IO流实现。
一个功能强大的websocket模块,符合 RFC 6455 并通过了 Autobahn测试(498个客户端测试和249个服务端测试)。????
net模块现在已经是非阻塞了,功能更加完善,并且提供了类似go语言的api。
V语言的图形模块现在不仅仅只有OpenGL,还有Metal/DirectX哦~????
V语言现在可以通过WASM在浏览器运行,并通过它翻译为JavaScript代码
支持通过Github Actions自动构建和部署。
简洁方便的数组排序方法: users.sort(a.name>b.name) 。
针对vfmt的大量修复和改进,现在可以安全地在任何v源文件使用它了。
一个用于分析项目并查找潜在错误的新工具: v vet
新的term.ui模块,用于动态终端UI界面的构建示例编辑器。
早期的iOS和Android支持。
简练的泛型语法:foo<int>(5) => foo(5)。
内置的编译时JSON序列化程序,现在支持 time.Time 。
允许接口定义字段(fields),而不仅仅是方法(methods)。
支持通过$env('ENV_VAR')来获取编译时的环境变量。

更多更新细节及内容,请移步更新日志

最新版本:0.3
自2020年12月发布0.2版以来,有 5769 个提交被推送到 master 分支,有 1697 个 bug 被修复。V 0.3 于2022年7月上旬发布,从现在开始,Vlang 将每隔 4 个月做一次重大更新。部分更新内容如下:
通过 C2V 进行 C 到 V 的转译:v translate file.c
在 V、cgen 和 C 互操作中进行了大量的错误修正,以允许运行转译好的 DOOM.v
用 V 编译器构建的程序不再默认泄漏内存
Vlang Closures 支持所有的操作系统
Option 和 Result 现在是独立的类型,旧的代码将在未来 1 年内可以继续正常使用
在类型检查器中增加了数百个新的检查
所有 V 的后端都被分割成独立的进程,因此构建 V 的速度提高了 26%。
ustring 已被 []rune 所取代
Maps 现在可以有非字符串的键
C 后端现在是并行的(目前只有 cgen 部分)
大量的编译器源代码的清理和优化。根据 fast.vlang.io 的数据,编译器的速度提高了~30%
更好的编译器源代码组织
V 的整数类型的命名现在更加一致了: byte 已被改名为 u8,旧的代码将在未来 1 年内可以继续正常使用
错别字检测器现在高亮显示了建议的名称,从而使其更加明显
datatypes 模块现在有 Heap, Queue, Stack, BSTree, LinkedList
vlib 现在有一个 TOML 解析器,与 TOML 1.0 完全兼容
在 V.js 后端做了很多工作,包括图形库,它已被移植到 V.js
现在可以通过使用数组中的每个单独元素来进行更复杂的数组初始化 ([]int{init: it})
V 中加入了移位运算符 >>> 和 >>>=(它们的工作原理与 Java 中的完全一样)
nofloat 选项,这对编写内核和没有 FPU 的嵌入式系统很有用
TCC 现在与语言捆绑在一起,这允许在不依赖外部 C 编译器的情况下构建 V 程序
Null 可以只在 unsafe 的情况下使用
新模块 compress.gzip
大量的 net/ net.http/ vweb 修正
Go2V 转译器已经由社区启动,并且已经可以转译简单的程序
Go 后端的早期版本( v -b go -o file.go file.v)
引入 isize 和 usize 类型,弃用 size_t,改用 usize
添加 datatypes 和 datatypes.fsm 模块
支持 IPv6、泛型接口支持、修复泛型中的更多错误。

最新版本:0.4
V语言(Vlang) 0.4.1 已于2023年9月上旬发布。主要变化集中在改进语言特性、解析器、标准库,以及编译器内部相关的变化等。
实现 Enum.from_string(name string) 将字符串转换为枚举值
禁止使用未初始化的函数指针
使用默认 expr 修复匿名结构
支持使用 const 作为枚举值
禁止将静态函数声明为方法接收者 (method receivers)
修复 for i++; i<10; i++ {
详情查看发行说明

V 语言在2023年9月有 lambdas 了。根据其仓库的 PR 合并信息,目前已添加对短 lambda 表达式的支持,例如:
a.sorted(|x,y| x > y)

示例代码
使用函数/表达式进行回调,从外部源获取它们的值

import os
fn f( cb fn() string ) string { return cb() }
dump( f(fn() string { return os.args[0] }) )

// the same, just shorter:
dump( f(||os.args[0]) )

打印结果如下:
[a.v:5] f(anon_fn): /v/vnew/a
[a.v:6] f(|| os.args[0]): /v/vnew/a


官方主页:
The V Programming Language
V Programming Language On Github
V语言中中文参考
V语言中文网之V语言简介
V语言开发文档之V语言简介