盒子里有什么东西?

2021-04-19 23:34:10

首先,这一建议很少见到,而且,无论谁说,谁都说它会消失两天,所以胜利,是因为它,编译器可能会在线,如果它想要。

第二,如果没有很多背景,如果你没有工作知识,就没有很多背景,这句话是Utternonsense。作为Java开发人员,您可能会想知道我们是否尝试将数字转换为对象(我们的arenot)。事实上,即使是作为铁锈开发商,您也可能刚刚接受了拳击只是一个生命的事实。

它'只是我们有时要做的一件事,所以编译器停止疯狂的atus,突然开始工作。那个'不一定是一个坏话。 ' s只是如何良好的编译器诊断,它可以只是tellyou"在那里抱着朋友,我真的认为你想要盒子",你可以复制和粘贴解决方案,拼图是破解。

但!仅仅因为我们可以在不知道它的情况下获得很长一段时间,而不是意味着我可以抵抗甜蜜的甜蜜诱惑,解释解释的甜蜜甜蜜的细节它实际意味着什么,所以'究竟是什么&#39 ;重申在这篇文章中。

然而,在我们这样做之前,让我们看看一个简单的例子,我们可能会被一位善意的同事们所做的,因为它,"只是框"

每当调用货物新建时,它会生成一个简单的"你好世界"应用程序,看起来像这样:

它是纯洁的,无辜的,没有任何可能失败的东西,这很好。

$ Cargo Run编译Whatbox V0.1.0(/ home / amos / ftl / whatbox)完成的dev [未通过+ debuginfo] target 0.47s运行`target / debug / whatbox`hello,world!

read_to_string可能会失败!并且它为什么它返回结果< string,e>而不仅仅是一个字符串。

而且,它也是为什么我们需要调用.unwrap(),从结果< string,e>要么:

$货物运行--quietthread' main'闹剧'被叫`结果:: unwrap()`在`err` value:ore {代码:2,种类:notfound,message:"没有这样的文件或目录" }',src / main.rs:2:59注意:使用`rust_backtrace = 1`环境变量来显示回溯

但是,假设我们想在函数内读取一个字符串。我们自己的功能。

但它不是我们想要的代码。请参阅,Read_issue函数Feelslike"图书馆代码"现在,它在我们的应用程序中,但我可以看到函数分成自己的箱子,也许是一个名叫Linux-Info或其他东西的箱子,因为它对其他应用程序有用。

所以,即使它在与主要功能相同的箱子中,我也不会在read_issue中引起恐慌的,我在迪斯科舞厅中感到舒适地引起恐慌。

相反,我想我也希望read_issue返回结果。因为结果< t,e>是一个枚举,可以代表两件事:hassucceeded的操作(我们得到一个t),或者它失败了(我们得到了一个e)。

我们知道,当操作成功时,我们得到一个字符串,所以我们知道要选择T.但问题是:我们选择了什么?

fn main(){println! (" {}",read_issue().unwrap())} //是什么是`E`应该是什么? 👇fn read_issue() - >结果<字符串,E> {std :: fs :: read_to_string(" / etc /问题")}

那个问题,那个具体的问题,不是我们真正拥有的一些其他语言的东西,比如...... ECMAScript!我的意思是,javascript!

从&#34导入{readfilesync}; fs" ; function main(){let问题= readissue();安慰 。日志(`$ {问题}`); }函数readissue(){readfilesync(" / etc / i-do-not-witt-witts"); } 主要的 ( ) ;

$节点js / index.mjs节点:FS:505 HanderErrorfrombinIning(CTX); ^错误:Enoent:没有这样的文件或目录,打开' / etc / i-do-not-witts'在Readfilesync(node:fs:401:35)在Readissue(文件:///home/amos/ftl/whatbox/js/index.mjs:9:5)在主要(文件://home/amos/ftl/whatbox/js/index.mjs:4:17)文件:///home/amos/ftl/whatbox/js/index.mjs:12:1在modulejob.run(节点:内部/模块/ ESM / module_job:154:23)在Async Loader.import(Neuts:Modules / ESM / Loader:177:24)在Async Object.LoadM中(节点:内部/流程/ ESM_Loader:68:5){errno:-2,syscall:'打开',代码:' enoent',路径:' / etc / i-do-not-ides& #39;}

我们不必担心阅读是否可以担心阅读可以或不能抛出:

好吧,也许我们应该!也许我们应该在一个尝试中包装它,只需从任何抛出的任何例外都可以释放。但我们不得不。我们的编程愉快地愉快地散步。

在Go,没有例外,但通常有一个指示在其签名中可能会失败。

包主要导入(" log"" os")func main(){问题:= readissue()日志。 printf("问题=%v"问题)} func readissue()字符串{bs,_:=操作系统。 ReadFile("")返回字符串(BS)}

包主要导入(" log"" OS")func main(){//我们获得了两个值,包括`err`问题,err:= readissue()// ...如果err!= nil {// ......并处理日志,我们应该检查nil-ness。 Fatalf("致命错误:%+ v",err)}日志。 printf("问题=%v"问题)} func Readissue()(字符串,错误){bs,err:=操作系统。 ReadFile("")//同样,'readfile`是一个多价值的返回,所以我们需要//首先检查`err`:如果err!= nil {返回"& #34;,err} //只有在这里我们知道读取文件实际上成功:返回字符串(BS),nil}

$ go运行go / main.go2021 / 04/17 20:47:37致命错误:打开:没有这样的文件或directoryexit状态1

但是,请注意,它没有告诉我们在代码中的何处是错误的,而JavaScript / node.js版本。

在那里有一个解决方案,但是bydefault开箱即用,go错误不会捕获堆栈痕迹。

然后是那里的生锈,这是三者中最严格的rust,迫使USTO声明一个函数可以失败,强迫我们处理可能在函数中发生的任何错误,但也迫使我们描述&#34 ;有可能的错误值是有可能的。

$ node js / index.mjsnode:内部/进程/ ESM_LOADER:74内部屏障('错误')。triggerung ackexception(^ woops(使用`node -trace-undaught ...`来显示异常的位置扔了)

这不是一个好主意。大多数情况下,因为那么我们不会得到堆栈迹象。

$节点js / index.mjs文件:///home/amos/ftl/whatbox/js/index.mjs:7抛出新的错误(" Woops"); ^错误:在Readissue(文件:///home/amos/ftl/whatbox/js/index.mms:7:11)在main(文件:///主页/ amos / ftl / whatbox / js / index。 MJS:2:17)AT文件:///home/amos/ftl/whatbox/js/index.mjs:10:1在modulejob.run(节点:内部/ modules / Esm / module_job:154:23)在异步Loader.import(Node:内部/模块/ ESM / LOADER:177:24)在ASYNC Object.LoadMSM(节点:内部/进程/ ESM_Loader:68:5)

至于去。你可以' t只是说你'重新返回一个错误,返回一个字符串。那个'好。

$ go / main.go#命令行 - argipsge / main.go:17:13:不能使用" woops" (类型字符串)作为返回参数中的类型错误:字符串不实现错误(缺少错误方法)

无论你返回什么都是错误的错误,而且有一个替代方案:

// new返回格式为给定文本的错误。 //即使文本是相同的,每个对新的呼叫也返回一个不同的错误值。 Func new(文本字符串)错误{return& errorstring {text}}

这实现了错误界面。所有接口都要求返回字符串的错误()方法:

这并不是说去的错误处理是在公园散步。

几乎所有关于Go的感觉几乎所有文章都指出了这一比特:它'虽然太容易忽视了,或者"忘记处理"去错误:

Func Readissue()(字符串,错误){BS,ERR:= OS。 ReadFile(" / etc /问题")err = os。 writefile(" / tmp / moss-copy" bs,0o644)如果err!= nil {return"",err} return字符串(bs),nil}

Woops!没有警告,没有什么。如果我们无法读取该文件,那么永远错误地错误。这里的问题当然是返回"多件事":"成功价值"和#34;误差值"和它'在粉红色发誓不要触摸成功价值,如果你verven' t先检查了错误值。

并且这个问题并不存在于一种语言中的语言 - 一个生锈结果,结果:: OK(t),或结果:: err(e),从不兼而有之。

然后我们可以将其作为错误返回。因为错误是一个接口,而* naugtyerror有一个错误的方法返回一个字符串,一切都很紧身,繁荣,组成,好!

但是,如果我们不小心返回了一个类型的* naugtyerror的价值,那么justhappens就是零,好吧......

包主要导入(" log")func Readissue()(字符串,错误){var Err * naughtyError日志。 Printf("(在Readissues中)是错误的吗?%v",err == nil)返回"",err} func main(){问题,err:= readissue()日志。如果err!= nil {log,printf("(主要)是err nil?%v",err == nil)。 Fatalf("致命错误:%+ v",err)}日志。 printf("问题=%v"问题)} //类型naughtyerror struct {} func(ne * naugtyerror)错误()字符串{return" ob no"}

$ go运行go / main.go2021 / 04/17 21:08:08(在Readissue)是错误的吗? True2021 / 04/17 21:08:08(主要)是错误吗? False2021 / 04/17 21:08:08致命错误:oh noxit状态1

第一个问题,"忘记检查nil"很容易理解。韦尔托你错误的地方。只是不要忘记检查一下。它易于Fitinto One' Go的心理模型,这是真正简单的宣传。

我们上次顽皮的示例计划中有两个错误的值。其中一个比例等于nil,另一个没有。

包主要导入(" log""不安全")func readissue()(字符串,错误){var err * naughtyerror日志。 printf("(readissuee)nil?%v,size =%v",err == nil,不安全。sizeof(错误))返回"",err} func main( ){问题,err:= readissue()日志。 printf("(主要)nil?%v,size =%v",err == nil,不安全。如果err!= nil {log {log。 Fatalf("致命错误:%+ v",err)}日志。 printf("问题=%v"问题)} //类型naughtyerror struct {} func(ne * naugtyerror)错误()字符串{return" ob no"}

进口不安全的软件包可能是不可移植的,不受兼容性指南的保护。

......但我们在这里做什么是完全无害的。重要的位,正如Iyunderstand,就是作为一个去开发人员,你'重申不应该关心。

你'重申不应该看这些东西。 Go很简单!字节切片arstrings! Go没有指针算术!谁关心一种类型的态度!

直到你照顾,然后,嗯,你自己'他们自己。和#34;使用不安全" isexactly在这里自行。但它'好吧。我们'所有这些都在我们的唯一。

$ go运行go / main.go2021 / 04/17 21:19:12(在Readissue)nil?真实的,大小= 82021/04/17 21:19:12(主要)零? FALSE,SIZE = 162021/04/17 21:19:12致命错误:OH Noxit状态1

这是一个示例,给出足够的时间,人们可以自己弄清楚自己的选择。但是当面对面坠落时,当伊萨已经有一段时间时,它就是......令人费解的。

指针的零值为nil,所以它等于nil。和我们'在64位Linux上(井,i' m),因此指针的大小为64位或8个字节。

如果你'从一台byte isn' t 8位的机器读取这一点,请发一张照片。

第二行更令人惊讶 - 不仅它不等于零,而且,它也是两倍的两倍。

包主要导入(" log")func main(){var err错误err =(* naugtyerror)(nil)日志。 printf("%v" err)err =(* niceError)(nil)日志。 printf("%v" err)} type naugtyerror struct {} func(ne * naugtyerror)错误()字符串{return" ob"} type niceerror struct {} func(ne * niceError)错误()字符串{返回"何浩浩!"}

多么愉快的假期主题错误。我们有两个零价值观,他们涉现不同的东西!

因为这些值都是nil!但是,uhhh充当interfacevalue(对于错误界面),它们的行为方式不同!

因此错误界面值的大小是16个字节,因为...有......' stwo指针!

"悲伤和#34;它,即。要从界面类型转到混凝土类型:

包主要导入("错误"" log")func showtype(错误错误){//👇越震惊的操作如果_,好的话:= err。(* naughtyerror);好的{log。 printf("有一个* naugtyerror")}如果_,好的:= err。(* niceError);好的{log。 printf("得到了一个* niceError")} else {log。 printf("得到了另一种错误")} func main(){showtype((* naugtyerror)(nil))showtype((* niceError)(nil))showtype(错误。新(&# 34;"))} type naugtyerror struct {} func(ne * naugtyerror)错误()字符串{return" ob"} type nicereror struct {} func(ne * niceError)错误()字符串{返回"何浩浩!"}

$ go / main.go2021 / 04/17 21:33:48 GOT a * naugtyerror2021 / 04/17 21:33:48 GOT A * NiceError2021 / 04/17 21:33:48得到了另一种错误

啊,所以神秘解决了!一个指针的值,一个指针为类型:8个字节,每个,一起,16个字节。

但是......还有一个标准错误类型。除了生锈,资本化并非意味着"私人或公共和#34; (那里有关键字)。相反,所有类型的所有类型都是大写的,所以它' s不是错误,它' s错误。

//👇我们在这里导入它使用std ::错误::错误; fn main(){println! (" {}",read_issue().unwrap())} //并使用它👇fn read_issue() - >结果<字符串,错误> {std :: fs :: read_to_string(" / etc /问题")}

$货物跑步 - Quietwarning:没有明确的“dyn`的特质物品被贬值 - > src / main.rs:7:35 | 7 | fn read_issue() - >结果<字符串,错误> {| ^^^^^^帮助:使用`dyn`:`dyn错误` =注意:`#[warn(baren_trait_objects)]`默认情况下(省略休息)

哦,不,警告!它说要使用dyn关键字。好吧,谁是ITO对象,让' s使用dyn关键字。

$ Cargo Run --quieterror [E0277]:类型的大小为`(Dyn STD ::错误:: error +'静态)`不能在编译时已知 - > src / main.rs:7:20 | 7 | fn read_issue() - >结果<字符串,dyn error> {| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^' t在编译时已知尺寸| ::: /home/amos/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs:241:20 | 241 | PUB枚举结果< t,e> {| - 在“STD ::”结果::结果:结果界定的界限= help:trait`大小`未实现`(dyn std :: serrer :: serror +'静态)`错误:由于上一个错误而中止

使用std ::错误::错误; fn main(){println! (" {}",read_issue().unwrap())} //👇fn read_issue() - >结果<字符串,框< Dyn错误> > {std :: fs :: read_to_string(" / etc /问题")}

$货物运行 - quieterror [e0308]:无匹配类型 - > src / main.rs:8:5 | 7 | fn read_issue() - >结果<串,框< dyn>> {| ---------------------------------结果::结果<串,框< :: errat +'静态)>>“因为返回type8 | std :: fs :: read_to_string(" / etc /问题")| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^预计struct`box`,找到struct` std :: io ::错误'| =注意:预期enum`STD ::结果::结果< _,box<(dyn std :: serr :: error +'静态)>>`找到enum` std ::结果::结果< _,std :: io ::错误>`错误:由于上一个错误而中止

我们走了。现在我们甚至是re。这是最接近的我们' ll到来的守则。

好吧,暂时,你可以对它作为指针进行思考。

struct myereror {value:u32,} fn main(){让e = myError {value:32};让e_ptr:* const myError =& e; print_error(e_ptr);} fn print_error(e:* const myError){如果e!= std :: ptr :: null(){println! (" myError(value = {})",不安全{(* e).value}; }}

fn print_error(e:* const myError){如果e!= std :: ptr :: null(){//👇println! (" myError(value = {})",不安全{(* e).value}; }}

为什么取消引用指针不安全?好吧,因为它可能是空! ORIT可能指向没有落在一个区域内的地址;对于当前正在运行的程序而言,它会导致ASEGING FOURT。

struct myereror {value:u32,} fn main(){让e = myError {value:32};让e_ptr:* const myError =& e; //👇没有不安全! DBG! (std :: mem :: size_of_val(& e_ptr)); print_error(e_ptr);} fn print_error(e:* const myError){如果e!= std :: ptr :: null(){println! (" myError(value = {})",不安全{(* e).value}; }}

并且,正如所预期的那样,指针的大小是8字节,因为我' m instwriting从linux 64位。

但是:如果构建指针值是安全的,请解释它(从它指向的内存读取或写入它)不是。

struct myereror {value:u32,} fn main(){让e = myError {value:32};让e_ref:& myerror =& e; DBG! (std :: mem :: size_of_val(& e_ref)); print_error(e_ref);} fn print_error(e:& myError){println! (" myError(value = {})"(* e).value);}

......但它们也非常安全地对解除引用,因为它可以保证它们指向有效内存:在安全的代码中,无法构建无效的参考,或者在释放估值后保持参考一些值。

事实上,它如此安全,我们甚至需要使用*运营商才能使用:我们可以依靠" autoderef&#34 ;:

现在,关于安全的快速记录:你'请注意,我只是说" Insafe代码,无法构建无效的参考文献"

struct myerror {value:u32,} fn main(){让e:* const myError = std :: ptr :: null(); // ooooh否否。犯罪! 👇让e_ref:& myerror =不安全{& * e}; DBG! (std :: mem :: size_of_val(& e_ref)); print_error(e_ref);} fn print_error(e:& myError){println! (" myError(value = {})",e.value);}

这个想法是,如果所有不安全的代码都是声音,那么所有安全的码头都安全。

而且你有很多少量"不安全"代码比你"安全"代码,这使得审计更容易。它也是非常可见的,具有明确的不安全块,不安全的特征和不安全的函数,所以它'很容易静态地确定不安全的码头 - 它' s不只是"你进口禁止包"你进口禁止包&#34 #34;

最后,在那里的工具如miri interpreter,那就帮助了不安全的代码,就像那里的c / c ++的消毒者那里,没有那些安全/ unsafeplit。

所以,我们看到两种"指针"到目前为止,迄今为止:原始指针,AKA * const t(及其兄弟姐妹,* mut t)和参考(& t和& mut t)。

在Go,当您对对象的指针时,可以使用它。 YouCan只要你想要,你就可以把它推到地图上 - 即使是最初是释放的,你也是,你作为一个函数,你可以伸出一个指向该对象的指针,可以扩展寿命

......