Rust 与信息学三定律( 三 )


正确性的另一部分是没有不确定的行为,安全问题和坠毁风险 。Rust通过使类型系统意识到所有权来在没有垃圾收集器的情况下管理内存 。资源由一个单一的变量绑定拥有,并且可以通过将变量传递给函数来转移所有权:Rust称其为移动资源(在某种意义上类似于C ++的move语义) 。
当变量绑定超出其词法范围时,它所拥有的资源将被删除-又名,已释放(可以通过将其移入遗忘而更早地删除该值) 。尽管它们可以证明是"活着的",但可以借用共享的方式不变地或以排他的方式可变地借用价值 。这类似于C ++引用,但是它允许Rust编译器证明不会对给定的数据结构进行共享,可变的访问,并且开发人员不必担心一件事 。
最后但并非最不重要的一点是,Rust通过确保在线程之间不共享非线程安全值来保护开发人员免受许多并发问题的困扰 。
有很多资源可以了解Rust的所有权和借用,但是我将展示一个简单的示例 。请注意,所有权的复杂性更高,特别是某些类型可以选择复制而不是移动的事实 。例如,"拥有"一个整数没有意义,因为它是如此之小以至于可以被简单地复制 。Rust编译器也在不断发展,变得更加智能,并允许更直观的代码 。
在上面的代码段中,创建了一个名为s的新字符串,并将其传递给函数foo 。因为foo接受一个String而不是对String的引用,所以它获取了所有权而不是仅仅借用它:将String移到foo 。后来,仍然主要在尝试打印s时,Rust编译器会抱怨资源s已绑定,因此不再可用 。该程序将无法编译 。
事实证明,foo返回一个不变的s,但是编译器不知道它,并且任何开发人员也不会只看主函数,foo的签名和String的特征 。知道这些元数据就足以知道foo拥有所有权,而我们对该资源的访问也会丢失 。
为了使我们的程序编译,我们可以简单地打印newS 。Rust甚至让我们再次称它为s,这很棒,因为无论如何将s传递给foo后,s都不可用! 下面的程序先打印" Bar",然后打印" Foo" 。
查看foo,它还会在名为s2的变量下创建另一个String 。超出范围时将被丢弃 。到目前为止,这看起来很像自动内存管理,用于在C ++中分配给堆栈上的结构或Java中由GC管理的引用句柄 。区别在于,资源(例如堆栈或堆分配的内存)始终只有一个所有者 。
在这里,在foo中返回s2而不是s将把s2返回给调用者,程序将打印" Bar"" Bar" 。顺便说一句,如果您想知道为什么在调用println!之后s2仍然可以使用,那是因为它是println !! 只借了它! 最后,惊叹号一目了然 。是一个宏 。
最后,没有必要在此处分配字符串,因为" Foo"和" Bar"常量已经在二进制文件中 。Rust可以直接指向那里并获得我们可以借用的"切片" 。我们使用称为&str的类型,它可以是借来的String或切片!
可维护性所有权系统不仅使程序执行安全,而且我认为这也使代码更易于维护和重用 。一目了然的功能或词法范围,Rust开发人员可以确定其变量的属性 。他们可以构建API,以更准确地传达应如何使用它们,并从类型检查器中寻求帮助 。
让我举一些例子 。在C语言中,编译器不知道函数返回的指针有效的时间 。在垃圾回收语言中,您可以存储函数返回的所有引用,并防止释放潜在的大块内存 。许多Java库随附文档,描述了什么时候可以安全地调用哪些方法,对象将处于有效状态的时间长短,并通过异常强制执行这些规则,或者让您处理未定义行为的后果 。当涉及多线程时,这变得更加困难 。Rust使得可以返回在编译时已知的给定生命周期内可能有效的引用,提供副本或提供您愿意时可以处置的共享引用 。
Rust附带了Cargo,这是一个命令行实用程序,用于构建,管理依赖项(称为Crates),运行测试,修复警告等 。拥有社区认可的构建工具意味着可以将精力集中在那里 。它有助于Cargo的开发人员做出不错的选择,例如支持使用开箱即用的包装箱进行语义版本控制,使用人类可读和可编辑的配置格式(TOML)或支持可复制的版本 。还有rustfmt,这是一种自动格式化程序,可防止浪费时间手动格式化源文件以及关于tabs-vs-spaces的无休止的争论(剧透警告:赢得了4个空格) 。
尽管如此,Rust的工具仍在开发中 。Java开创了20年的先机,但是语言本身非常适合工具 。IDE应该如何在宏中支持DSL? 时间会证明一切 。有一个官方语言服务器,该服务器具有VSCode和Eclipse集成 。还有一个IntelliJ IDEA插件 。


推荐阅读