?
新手必看-如何安装配置vlang运行环境(linux,macOS篇)
前置条件
发稿截止前只有Linux 或者 macOS系统能编译通过。
你需要安装clang 或gcc
如果是macOS上需运行xcode-select --install,如果没有安装XCode或XCode工具,请安装一下。
如果是centos,需要检查是否安装有clang
# which clang
如果没有,请安装
# yum install -y clang
开始安装
笔者本人使用的是macOS,以下是我的环境:

1.从github克隆vlang项目代码
# git clone https://github.com/vlang/v
# cd v/compiler
# make

这里提示wget 命令没有,使用brew 安装
# brew install wget
没有brew 的同学请先安装brew
# /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
然后再执行make 命令即可。
到此,macOS会正常编译通过,而linux可能会报以下错误:

解决办法:删除cc ,创建软连接cc->clang ,使用clang代替cc来编译,命令如下:
# cd /usr/bin
# sudo rm cc
# sudo ln -s clang cc
然后再回到compiler 文件夹,执行命令:
# make clean && make
?
循序渐进:V语言0.1.3版本更新,vlang命令介绍
??V语言中文网??|???963?|??? 2019-06-26 23:35:14
6月26日,vlang官网(https://vlang.io)已更新版本到0.1.3 ,同时发布了linux平台及macOS平台编译好的二进制文件供下载,windows平台暂无,不久会发布。
该版本修复了vlang之前编译会报错的+= 、*= 等运算符,同时也更新了v命令,现整理v命令如下:
1.直接运行v,将进入交互式编程环境(REPL )

2.编译.v 文件
v file.v
这里将file.v 文件编译为二进制可执行文件file ,如需运行,请在执行./file 。
默认情况下,生成的可执行文件的名称和.v 文件名相同,如果在编译时修改,可执行v -o <程序名> file.v
3.编译并运行.v 文件
v run file.v
4.细心的同学会发现,每次编译完.v 文件后,不仅会生成二进制可执行文件,同时还会生成一个file.dSYM 的文件夹,对于有洁癖的同学来说时难以忍受的,其实只要运行以下命令就会只生成纯粹的可执行文件,不会生成“烦人”的file.dSYM 文件夹
v -prod file.v
P.S:以上-o 、-prod 参数可以混合使用??
?
vpm
vpm是一个v语言包管理工具,它用v编写而成。
github地址:https://github.com/yue-best-practices/vpm
前置条件(Precondition)
- 安装
git ,因为目前包是通过git clone 的方式下载的。
- 配置
VROOT 环境变量,内容指向v语言源码路径。
安装(Install)
- 在任意目录下,
git clone https://github.com/yue-best-practices/vpm
- 执行命令
v -prod . 即可编译出vpm 可执行程序
命令(Commands)
命令/Command |
参数/Params |
释义/Description |
-v /version
|
? |
版本信息,目前版本是0.0.1 Show version,the current version is?0.0.1
|
init |
<project-name> |
创建.vpm.json 文件 Create the?.vpm.json ?file |
get |
<git-url> ?<pkg-name>
|
从<git-url> 中获取包。 Fetch package from the git repo. |
install |
? |
安装.vpm.json 文件中的包。 Install the package from the?.vpm.json ?file. |
-h /help
|
? |
显示帮助信息。 Show help information. |
clean |
? |
删除.vpm.json 文件。 Delete the?.vpm.json ?file. |
?
?
?
?
V语言简介
V语言是一种静态类型的编译型编程语言,它与Go类似,也受到?Oberon、Rust、Swift语言的影响。
V语言是一种非常简单的语言,阅读此文档将花费你大约半小时的时间来学习完几乎整个V语言。
尽管很简单,但它为开发人员提供了很多动力。 任何你可以用其他语言完成的事情,你都可以使用V语言来做。
?
V语言将在2019年6月开源发布。
V语言官网地址:https://vlang.io
V语言Github地址:https://github.com/vlang/v
?
Hello World
?
作为学习编程语言的传统,Hello World环节必不可少!
fn main() {
println(‘hello world‘)
}
函数用fn 声明。 返回类型在函数名称后面。 在这种情况下,main 不返回任何内容,因此省略了类型。
就像在C和所有相关语言中一样,main 方法是程序入口。
println 是V语言为数不多的内置函数之一,它将值打印到标准输出。
?
注释
V语言的注释中规中矩,和众多语言一样。
// 单行注释 /* 多行注释 /* 嵌套注释 */ */
函数
fn add(x int,y int) int {
return x + y
}
?
fn sub(x,y int) int {
return x - y
}
?
fn main() {
println(add(77,33))
println(sub(100,50))
}
?
?
同样,类型出现在参数的名称之后。
就像在Go和C中一样,函数不能重载。 这简化了代码并提高了可维护性和可读性。
?
?
变量
fn main() {
name := ‘Bob‘
age := 20
large_number := i64(9999999999)
println(name)
println(age)
println(large_number)
}
?
使用:= 声明和初始化变量。 这是在V语言中声明变量的?唯一方法。这意味着变量始终具有初始值。
变量的类型是从右侧的值推断出来的。要强制使用其他类型,请使用类型转换:表达式?T(v) 将值?v 转换为类型?T 。
与大多数其他语言不同,V语言中只允许在函数中定义变量。 不允许使用全局(模块级别)变量。
V语言中没有全局变量。
?
?
基本类型
?
V语言中共有20个基本类型,如下:
?
?
- 有符号整数:?
i8 、i16 、i32 ?、i64
- 无符号整数:?
u8 、u16 、u32 、u64
- 别名:?
byte (u8 )、?int (i32 )、?rune (i32 ,表示Unicode 代码点)
?
?
*请注意,与C和Go不同, V语言中int 始终是32位整数。
?
?
字符串(Strings)
fn main() {
name := ‘Bob‘
println(‘Hello,$name!‘)
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语言中,字符串是只读的字节数组。 字符串数据使用UTF-8编码。
字符串是不可变的。 这意味着?substr ?函数非常有效:不执行复制,不需要额外的分配。
连接运算符+ 需要两边都有字符串。 如果age是int,则不编译此代码,如下所示:
?
必须先将age 转为字符串:
println(‘age = ‘ + age.str())
?
或使用$ 符号进行字符串插入:
?
或者将?age 作为第二个参数传给println (*此方法尚未实现):
println(‘age = ‘,age) // TODO: not implemented yet
数组(Arrays)
fn main() {
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(names.contains(‘Alex‘)) // ==> "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,3] 是一个int 数组([]int )。
[‘a‘,‘b‘] 是字符串数组([]string )。
数组中所有元素的类型必须统一。[1,‘a‘] 将无法通过编译。
<< 运算符可以将元素追加到数组末尾。
.len 属性可以获取数组的长度。请注意,它是一个只读属性,用户无法修改。默认情况下,V语言中所有导出的属性都是只读的。
.contains(val) 方法可以检测数组中是否包含某元素,返回值bool 类型。
数据截取可以使用.slice(start,end) 、.left(pos) 、.right(pos) 方法。
?
?
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
fn main() {
a := 10
b := 20
if a < b {
println(‘$a < $b‘)
} else if a > b {
println(‘$a > $b‘)
} else {
println(‘$a == $b‘)
}
}
if 语句非常简单,和Go等众多语言类似。
但与类C语言不同的是,条件不需要() ,但{} 必须。
if 也可以被用作表达式:
num := 777
s := if num % 2 == 0 {
‘even‘
}
else {
‘odd‘
}
println(s) // ==> "odd"
In operator
in 可以检查数组是否包含元素。
nums := [1,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 语言优化了这样的表达式,因此如果上面的if语句产生相同的机器代码,则不会创建任何数组。
?
?
For循环
V语言中只有一个循环结构for 。
fn main() {
numbers := [1,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
}
?
for...in 循环用于遍历数组的元素。如果需要索引,则可以使用?for index,value in ?的形式。
?
fn main() {
mut sum := 0
mut i := 0
for i <= 100 {
sum += i
i++
}
println(sum) // ==> "5050"
}
?
这种形式的循环类似于其他语言中的?while ?循环。一旦布尔条件求值为false,循环将停止迭代。
同样,和if 类似,循环的条件没有括号,而循环体需要。
?
fn main() {
mut num := 0
for {
num++
if num >= 10 {
break
}
}
println(num) // ==> "10"
}
?
循环条件可以省略,这会导致无限循环。
?
fn main() {
for i := 0; i < 10; i++ {
println(i)
}
}
?
最后,还有传统的C风格循环。 它比while 形式更安全,因为后者很容易忘记因更新计数器而陷入死循环。
在这里i 不需要用?mut ?声明,因为它在每次循环中都被重新定义并赋值。
?
?
Switch
fn main() {
os := ‘windows‘
print(‘V is running on ‘)
switch os {
case ‘darwin‘:
println(‘macOS.‘)
case ‘linux‘:
println(‘Linux.‘)
default:
println(os)
}
// TODO: replace with match expressions
}
?
switch 语句是编写?if-else 语句的较短方法。 它运行第一种情况,其值等于条件表达式。
与C语言不同的是,V语言中不需要为每个匹配的代码块都添加break 语句。
?
?
结构体(Structs)
struct Point {
x int
y int
}
?
fn main() {
p := Point{
x: 10
y: 20
}
println(p.x) // Struct fields are accessed using a dot
}
?
structs 在堆栈上分配。 要在堆上分配结构并获取指向它的指针,请使用& 前缀:
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 July
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)
资源修饰符(Access modifiers)
?
结构体字段默认是私有的和不可变的(结构体也不可变)。 他们的访问修饰符可以用pub 和mut 来改变。 总共有5种可能的选择:
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)
例如,这是内置模块中定义的字符串类型:
struct string {
str byteptr
pub:
len int
}
从这个定义中很容易看出字符串是一个不可变类型。
内置字符串数据的字节指针根本不可访问。 len字段是公开的,但不是可变的。
fn main() {
str := ‘hello‘
len := str.len // OK
str.len++ // Compilation error
}
方法(Methods)
struct User {
age int
}
?
fn (u User) can_register() bool {
return u.age > 16
}
?
fn main() {
user := User{age: 10}
println(user.can_register()) // ==> "false"
?
user2 := User{age: 20}
println(user2.can_register()) // ==> "true"
}
V语言中没有类的概念,但是你可以在结构体?struct ?上定义方法(Methods)。
方法(Methods)是具有特殊接收器参数的函数。接收器出现在?fn 关键字和方法名称之间的参数列表中(和Go语言类似)。
在上面的示例中,can_register 方法具有名为?u 的?User 结构体的接收器。不是使用像?self 或?this 这样的名称,而是使用短名称,最好是所属结构体名称的首字母小写。
?
?
可变接收器及纯函数(Mutable receivers & pure functions)
struct User {
is_registered bool
}
?
fn (u mut User) register() {
u.is_registered = true
}
?
fn main() {
mut user := User{}
println(user.is_registered) // ==> "false"
user.register()
// TODO: Maybe force marking methods that modify the receiver with `!`
// user.register()!
println(user.is_registered) // ==> "true"
}
?
请注意,功能只能修改接收器。fn register(u mut User) 这样的做法无法通过编译。
这点非常重要,所以再次申明:V语言中函数是部分纯的,它们的参数永远不会被函数修改。
?
修改对象的另一种方法是返回修改后的版本(*待实现):
// TODO: this syntax is not implemented yet
fn register(u User) User {
return { u | is_registered: true }
}
?
user = register(user)
常量(Constants)
const (
PI = 3.14
WORLD = ‘世界‘
)
?
fn main() {
println(PI)
println(WORLD)
}
使用?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,g,b int) Color { return Color{r: r,g: g,b: b} }
?
const (
NUMBERS = [1,3]
?
RED = Color{r: 255,g: 0,b: 0}
BLUE = rgb(0,255)
)
?
fn main() {
println(NUMBERS)
println(RED)
println(BLUE)
}
V语言中没有全局变量,常量就更加有用了。
?
?
模块(Modules)
Vlang是一种模块化的语言,创建可重用模块是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 mymodule!‘)
}
?
在Vlang中,导出模块中的函数,需要使用pub 关键字。
你可以编写多个.v 文件在mymodule/ 中,编译模块也很简单,只要执行命令即可:
v -lib ~/code/modules/mymodule
使用模块中导出的函数也非常简单:
module main
?
import mymodule
?
fn main() {
mymodule.say_hi()
}
?
请注意,每次调用外部函数时都必须指定模块。 这看起来似乎很冗长,但它使代码更易读,更容易理解,因为它始终清楚从哪个模块调用哪个函数,特别是在大型项目的代码库中。
模块名称应简短,不超过10个字符。?循环导入是不允许的。
现在你可以在任何地方创建模块,也许这应该是标准化的(比如Go的GOPATH)。
所有模块都静态编译为单个可执行文件。
?
?
接口(Interfaces)
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())
}
?
fn main() {
dog := Dog{}
cat := Cat{}
perform(dog) // ==> "woof"
perform(cat) // ==> "meow"
}
?
和Go语言类似,结构体通过实现某接口中的方法来"实现"该接口,没有明确的声明,也没有像Java中的?implements 关键字。
?
?
枚举(Enums)
enum Color {
red,green,blue
}
?
fn main() {
mut color := red // TODO: color := Color.green
color = green // TODO: color = .green
println(color) // ==> "1" TODO: print "green"?
}
Option/Result types & error handling
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将Option和Result组合成一种类型,因此您无需决定使用哪种类型。
将函数“升级”为可选函数所需的工作量很小:您必须添加一个? 返回类型并在出现错误时返回错误。
如果您不需要返回错误,则只能return none 。 (TODO:none 还没有实现)。
这是V 中处理错误的主要方法。它们仍然是值,但是错误处理要简洁很多。
当然,错误是可以被传递的:
resp := http.get(url)?
println(resp.body)
http.get 返回的是?http.Response 可选类型。如果错误发生,将传播到调用函数,这里是导致main函数抛出异常。
上面代码是下面代码的简写:
resp := http.get(url) or {
panic(err)
}
println(resp.body)
V 没有办法强制打开一个可选项(比如Rust 的unwrap() 或Swift 中的! )。 你必须使用或{panic(err)} 代替。
?
?
泛型(Generics)
*七月份恢复
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最终会将?? 替换为<> 。
The initial version of V had generics because arrays and maps were built with them. Later I figured out a way to implement these data structures without generics. This simplified code greatly.
Since I no longer needed them,they were moved to a branch,because maintaining generics is tough. Generics will not be available when the language is released in June,but they should be back by July. 摘录自V语言作者
Alex
?
?
并发(Concurrency)
?
Vlang的并发模型与Go非常相似。 要同时运行foo() ,只需使用go foo() 来调用它。 目前它在一个新的系统线程中运行该函数,很快就会实现goroutines 和调度程序。
?
?
?
JSON解析(Decoding JSON)
import json
struct User {
name string
age int
}
?
fn main() {
data := ‘{ "name": "Frodo","age": 25 }‘
user := json.decode(User,data) or {
eprintln(‘Failed to decode json‘)
return
}
println(user.name)
println(user.age)
}
?
JSON现在非常流行,这就是内置JSON支持的原因。
json.decode 函数的第一个参数是要解码的类型。 第二个参数是要解析的json字符串。
V语言生成用于json编码和解码的代码,没有使用反射,这样性能更好。
?
?
测试(Testing)
// 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。
?
?
内存管理(Memory management)
Vlang中没有垃圾收集或引用计数,在编译期间清理它能做的事情,比如:
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),50)
...
}
?
字符串不会转义draw_text ,因此在函数退出时会清除它们。
事实上,前两个调用根本不会产生任何分配。 这两个字符串很小,V将为它们使用预分配的缓冲区。
对于更复杂的情况,需要手动内存管理。 这将很快修复。
V将在运行时检测内存泄漏并报告它们。 要清理(例如)数组,请使用free() 方法:
numbers := [0; 1000000]
...
numbers.free()
调用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)
}
关键词
V语言目前共有以下21个关键词:
break
const
continue
defer
else
enum
fn
for
go
goto
if
import
in
interface
match
module
mut
or
return
struct
type
?
?
有限的运算符重载
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的简单性和可预测性的理念。 但由于科学和图形应用程序也属于V的应用范围,因此为了提高可读性,运算符重载非常重要:
a.add(b).add(c.mul(d)) ?的可读性远远不及?a + b + c * d 。
为了提高安全性和可维护性,运算符重载有几个局限性:
- 只能重载
+ 、- 、* 、/ 运算符。
- 不允许在重载函数内调用其他函数。
- 运算符函数无法修改其参数。
- 两个参数必须具有相同的类型(就像V中的所有运算符一样)。
?
?
转换C/C++代码到V语言
V可以将您的C/C++代码转换为人类可读的V语言代码。让我们先创建一个简单的程序?test.cpp :
?
#include <vector>
#include <string>
#include <iostream>
?
int main() {
std::vector<std::string> s;
s.push_back("V is ");
s.push_back("awesome");
std::cout << s.size() << std::endl;
return 0;
}
?
运行命令?v translate test.cpp,然后V将生成文件test.v :
?
fn main {
mut s := []string
s << ‘V is ‘
s << ‘awesome‘
println(s.len)
}
?
An online C/C++ to V translator is coming soon. 摘录自V语言作者
Alex
交叉编译(Cross compilation)
V语言中跨平台编译(交叉编译)只需要简单执行以下命令:
v -os windows .
或者:
v -os linux .
(目前不支持macOS交叉编译)
如果项目中没有用到C的依赖库,上面的命令就是你需要做的,甚至在使用ui模块或使用gg的图形应用程序编译GUI应用程序时也能正常工作。
?
?
转载自:https://www.v-lang.cn/
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|