30分钟内曲折

2021-01-03 17:04:17

命令zig run my_code.zig将编译并立即运行您的Zigprogram。这些单元格中的每一个都包含一个可以尝试运行的zig程序(其中一些包含编译时错误,可以将其注释掉以进行播放)

//注释看起来像这样,然后转到pub fn main()行的末尾void {}

您可以使用内置的@import并将命名空间分配给const值,从标准库中进行导入。必须为zig中的几乎所有内容明确分配其标识符。您也可以这样导入其他zig文件,以及使用@cImport以类似方式导入C文件。

const std = @import(" std"); pub fn main()void {var x:i32 = 47; //声明" x" i32类型的值为47。std.debug。 print(" x:{} \ n&#34 ;,。{x});}

pub fn main()void {const x:i32 = 47; x = 42; //错误:无法分配给常量}

Zig非常挑剔,不会让您在外部范围内隐藏标识符,以免引起混淆:

const x:i32 = 47; pub fn main()void {var x:i32 = 42; //错误:重新定义' x'}

全局作用域中的常量默认情况下是编译时" comptime"值,如果您省略类型,则它们是comptime类型的,并且可以转换为您的运行时值的运行时类型。

const x:i32 = 47;常数y = -47; // comptime整数。 pub fn main()void {var a:i32 = y; //将comptime常数强制转换为正确的类型var b:i64 = y; //将comptime常数强制转换为正确的类型var c:u32 = y; //错误:无法将负值-47转换为无符号整数}

如果稍后要设置它,则可以明确地选择使其保持未定义状态,但是如果您尝试使用它,zig将会出错。

pub fn main()void {var x:i32 = undefined; foo(x); //错误:使用未声明的标识符' foo'}

在某些情况下,zig将允许您省略类型信息(如果可以确定的话)。

const std = @import(" std"); pub fn main()void {var x:i32 = 47; var y:i32 = 47; var z = x + y; //声明z并将其设置为94。std.debug。 print(" z:{} \ n&#34 ;,。{z});}

pub fn main()void {var x = 47; //错误:类型为' comptime_int'的变量必须为const或comptime}

这是一个不返回任何内容的函数(foo)。 pub关键字表示该函数可从当前作用域导出,这就是为什么main必须是pub的原因。 Youcall的功能与大多数编程语言相同:

const std = @import(" std"); fn foo()void {std.debug。 print(" foo!\ n&#34 ;,。{}); //可选:return;} pub fn main()void {foo();}

const std = @import(" std"); fn foo()i32 {return 47;} pub fn main()void {var result = foo();标准调试。 print(" foo:{} \ n&#34 ;,。{result});}

fn foo()i32 {return 47;} pub fn main()void {foo(); //错误:表达式值被忽略}

const std = @import(" std"); fn foo(x:i32)void {std.debug。 print(" foo param:{} \ n&#34 ;,。{x});} pub fn main()void {foo(47);}

可以通过使用const关键字为结构命名来声明结构,它们可以不按顺序分配,也可以通过使用通常的点语法取消引用来使用它们。

const std = @import(" std"); const Vec2 = struct {x:f64,y:f64}; pub fn main()void {var v = Vec2 {.y = 1.0,.x = 2.0};标准调试。 print(" v:{} \ n&#34 ;,。{v});}

结构可以具有默认值;结构也可以是匿名的,并且可以强制转换为另一个结构,只要可以将所有值都标明:

const std = @import(" std"); const Vec3 = struct {x:f64 = 0.0,y:f64,z:f64}; pub fn main()void {var v:Vec3 =。{。y = 0.1,.z = 0.2}; //好的var w:Vec3 =。{。y = 0.1}; //错误:缺少字段:' z'标准调试。 print(" v:{} \ n&#34 ;,。{v});}

您可以将函数放入结构中以使其像OOP样式的对象一样工作。第一个参数是指向对象的指针,可以称为" Object-style&#34 ;,类似于Python具有自参数化成员函数的方式。典型的约定是通过调用变量self使这一点变得显而易见。

const std = @import(" std"); const LikeAnObject = struct {值:i32,fn print(self:* LikeAnObject)void {std.debug。 print(" value:{} \ n&#34 ;,。{self.value}); }}; pub fn main()void {var obj = LikeAnObject {.value = 47};对象打印();}

通过使用const关键字将枚举组分配为类型来声明枚举。

您可以将Enum的值设置为整数,但不会自动强制转换,必须使用@enumToInt或@intToEnum进行转换。

const std = @import(" std"); const EnumType =枚举{EnumOne,EnumTwo,EnumThree = 3}; pub fn main()void {std.debug。 print(" One:{} \ n&#34 ;,。{EnumType.EnumOne});标准调试。 print(" Two ?: {} \ n&#34 ;,。{EnumType.EnumTwo == .EnumTwo});标准调试。 print(" Three ?: {} \ n&#34 ;,。{@enumToInt(EnumType.EnumThree)== 3});}

zig具有数组,它们是具有编译时已知长度的连续内存。您可以通过预先声明类型并提供值列表来对其进行初始化。您可以使用数组的len字段访问长度。

const std = @import(" std"); pub fn main()void {var array:[3] u32 = [3] u32 {47,47,47}; //也有效:// var array = [_] u32 {47,47,47}; var invalid = array [4]; //错误:索引4在大小为3的数组之外。std.debug。 print(" array [0]:{} \ n&#34 ;,。{array [0]});标准调试。 print(" length:{} \ n&#34 ;,。{array.len});}

zig还具有切片,这些切片具有运行时已知的长度。您可以使用切片操作从数组或其他切片构造切片。与数组类似,切片具有一个len字段,该字段告诉您其长度。

尝试访问超出分片范围的操作会导致运行时恐慌(这意味着您的程序将崩溃)。

const std = @import(" std"); pub fn main()void {var array:[3] u32 = [_] u32 {47,47,47}; var slice:[] u32 = array [0 ..2]; //也有效:// var slice = array [0..2]; var invalid = slice [3]; //恐慌:索引超出标准std.debug。 print(" slice [0]:{} \ n&#34 ;,。{slice [0]});标准调试。 print(" length:{} \ n&#34 ;,。{slice.len});}

顺便说一句,我们已经将该东西传递给了std.debug.print的第二个参数匿名数组。在编译时,将分析参数的内容和类型,并将其匹配到您提供的(编译时)格式字符串,并且show zig知道如何使打印内容漂亮。

const std = @import(" std"); pub fn main()void {std.debug。 print(" {} \ n&#34 ;,。{1,2}); #错误:未使用的参数}

字符串文字是以NULL终止的utf8编码的const u8字节数组。 Unicode字符仅允许在字符串文字和注释中使用。

const std = @import(" std"); const string =" hello世界&#34 ;; const world =" world&#34 ;; pub fn main()void {var slice:[] const u8 = string [0 ..5];标准调试。 print(" string {} \ n&#34 ;,。{string});标准调试。 print(" length {} \ n&#34 ;,。{world.len});标准调试。 print(" null {} \ n&#34 ;,。{world [5]});标准调试。 print(" slice {} \ n&#34 ;,。{slice});标准调试。 print(" huh?{} \ n&#34 ;,。{string [0 ..7]});}

const std = @import(" std"); fn foo()[] const u8 {//注意函数返回一个切片return" foo&#34 ;; //但这是一个const数组。} pub fn main()void {std.debug。 print(" foo:{} \ n&#34 ;,。{foo()});}

const std = @import(" std"); fn foo(v:i32)[] const u8 {if(v< 0){return" negative&#34 ;; } else {return" non-negative&#34 ;; }} pub fn main()void {std.debug。 print(" positive {} \ n&#34 ;,。{foo(47)});标准调试。 print(" negative {} \ n&#34 ;,。{foo(-47)});}

const std = @import(" std"); fn foo(v:i32)[] const u8 {开关(v){0 =>返回"零"否则=>返回"非零" }} pub fn main()void {std.debug。 print(" 47 {} \ n&#34 ;,。{foo(47)});标准调试。 print(" 0 {} \ n&#34 ;,。{foo(0)});}

const std = @import(" std"); pub fn main()void {var array = [_] i32 {47,48,49};为(数组)|价值| {标准调试。 print(" array {} \ n&#34 ;,。{value}); }(数组)|值,索引| {标准调试。 print(" array {}:{} \ n&#34 ;,。{index,value}); } var slice = array [0 ..2];为(切片)|价值| {标准调试。 print(" slice {} \ n&#34 ;,。{value}); }(切片)|值,索引| {标准调试。 print(" slice {}:{} \ n&#34 ;,。{index,value}); }}

const std = @import(" std"); pub fn main()void {var array = [_] i32 {47,48,49}; var索引:u32 = 0; while(index< 2){std.debug。 print(" value:{} \ n&#34 ;,。{array [index]});索引+ = 1; }}

错误是特殊的联合类型,您表示函数可以通过加前缀来出错!到前面。您只需简单地将其返回就可以抛出错误,就像正常返回一样。

const MyError = error {GenericError,//只是标识符列表,例如枚举。 OtherError}; pub fn main()! void {return MyError.GenericError;}

如果编写的函数可能出错,则必须决定在返回时如何处理。 try是两个很常见的选项,它们非常懒惰,它们只是将错误转发为函数的错误。 catch可显式处理错误。

const std = @import(" std"); const MyError = error {GenericError}; fn foo(v:i32)! i32 {if(v == 42)return MyError.GenericError; return v;} pub fn main()! void {// //捕获陷阱并处理冒泡的错误_ = foo(42)catch | err | {标准调试。 print(" error:{} \ n&#34 ;,。{err}); }; //尝试不要在这里被激活。标准调试。 print(" foo:{} \ n&#34 ;,。{try foo(47)}); //这最终将导致main打印错误跟踪并返回非零_ = try foo(42);}

const std = @import(" std"); const MyError = error {GenericError}; fn foo(v:i32)! i32 {if(v == 42)return MyError.GenericError; return v;} //注意,wrap_foo没有错误是安全的!因为//我们处理所有情况,并且不返回错误。 fn wrap_foo(v:i32)void {if(foo(v))|价值| {标准调试。 print(" value:{} \ n&#34 ;,。{value}); } else |错误| {标准调试。 print(" error:{} \ n&#34 ;,。{err}); }} pub fn main()void {wrap_foo(42); wrap_foo(47);}

这是一个多重调度的示例(尽管您已经在std.debug.print中看到了这一点,也许现在您可以想象它是如何实现的:

const std = @import(" std"); fn foo(x:anytype)@TypeOf(x){//注意,此if语句发生在编译时,而不是运行时。如果(@TypeOf(x)== i64){返回x + 2; } else {return 2 * x; }} pub fn main()void {var x:i64 = 47; var y:i32 = 47;标准调试。 print(" i64-foo:{} \ n&#34 ;,。{foo(x)});标准调试。 print(" i32-foo:{} \ n&#34 ;,。{foo(y)});}

const std = @import(" std"); fn Vec2Of(comptime T:type)type {return struct {x:T,y:T};} const V2i64 = Vec2Of(i64); const V2f64 = Vec2Of(f64); pub fn main()void {var vi = V2i64 {.x = 47,.y = 47}; var vf = V2f64 {.x = 47.0,.y = 47.0}; 标准调试。 print(" i64 vector:{} \ n&#34 ;,。{vi}); 标准调试。 print(" f64 vector:{} \ n&#34 ;,。{vf});} 就是这样! 现在,您知道了相当不错的Zig。 我没有涉及的一些(非常重要)的事情包括: