AI写作智能体 自主规划任务,支持联网查询和网页读取,多模态高效创作各类分析报告、商业计划、营销方案、教学内容等。 广告
引用的生命周期不能超过所有者的生命周期,你必须在代码里明确指出引用不会活得比所有者长。为了强调这一点,rust通过创建引用,借一个值,你借什么值,最后一定要还。引用在底层不是什么特殊的东西,仅仅是个地址而已。 HashMap不是拷贝类型,因为它拥有动态分配的表,当它作为参数传递给函数时,整个结构移动到函数内部,导致变量变成未初始化,假如调用之后还要用这个变量,会发生错误。在函数里迭代HashMap会消费破坏掉整个HashMap的结构。 引用有2种: 共享引用允许你读,但是不能修改所有者,但是可以同时拥有很多共享引用。表达式&e生成一个共享引用,假如e的类型是T,则&e的类型就是&T,共享引用是拷贝类型。 可变引用可以读和修改所有者,但是不能同时拥有其他任何类型的引用。表达式&mut e生成一个可变引用,它的类型是&mut T,可变引用不是拷贝类型。 你可以认为共享引用和可变引用,是保证在编译期可以有多个读取器或者一个修改器,实际上这个规则不仅仅作用在引用上,还作用在所有者上,一旦有共享引用,它的所有者也不能修改它的值,它的值被锁了。一旦有可变引用,它就拥有独占的修改权,它的所有者就处于不可用状态,直到可变引用被释放。保持共享引用和可变引用分离是内存安全的保证。 引用是没有所有权的指针,给函数传递引用参数让所有者仍然拥有所有权,函数只是借用一会儿。 通过共享引用传递HashMap给函数进行迭代,迭代出来的key和value也是共享引用,HashMap本身的结构不会遭到破坏。 如果在函数里需要修改引用的数据,需要传递可变引用。 在rust中,通过&显式创建引用,并用*解引用,用&mut创建可变引用。由于引用被广泛引用,当用.操作符过去属性或者调用方法的时候会自动解引用。点操作符在需要(调用的方法是引用的方法)的时候还可以自动创建引用,比如调用向量的.sort()方法,会自动创建向量的&mut引用。 给一个已有指向的引用赋新值会让引用指向新的值,这和c++不同,c++里给一个引用赋值,会改变引用的值。 rust允许给引用创建引用,.点操作符会逐级查找引用,最后找到根的属性值或者方法。类似.操作符,比较运算符也会逐级查找根的值再进行比较。如果你真想比较两个多级引用本身,可以用标准库的函数std::ptr::eq,会比较它们的内存地址是否相同。 rust的引用绝不会为null,在rust里没有类似c和c++的NULL,对于引用,没有默认的初始化值,对任何类型在初始化之前都不能用。并且rust不会转换任何整数成引用类型,因此你不能把0转成引用。 c和c++经常用null来表示空值,比如malloc函数返回null表示申请不到内存;在rust中如果你需要一个可以为空的引用,你应该用Option<&T>类型,在底层,rust用None表示null,Some(r)表示非空地址,因此Option<&T>类型和c++的指针一样高效,Option类型要求你强制检查一个值是否为空。 c++只允许你用&引用在特定的一些类型上,rust允许你从任意类型借一个引用。&作用在表达式上创建一个匿名变量的引用,匿名变量的生命周期决定在引用上。假如你把匿名变量的引用赋值给一个变量或者复杂结构的一部分,rust就让这个匿名变量活得和引用变量一样长;否则匿名变量活到它所在块的末尾。 记住rust决不允许你写出产生垂悬指针的代码。假如你使用引用超过了匿名变量本身的生命周期,rust会在编译期报错,因此你可以修改代码给匿名变量一个名字以拥有合适的生命周期。 rust包括2种肥指针,切片就是肥指针,包括目标数据的地址和长度。另一种肥指针是trait对象,它包含一个值的地址和trait实现的指针,为了调用trait的方法。 生命周期是引用的延伸,为了让程序安全运行,生命周期完全是在编译期臆造的概念,在运行期,一个引用仅仅是一个地址而已。生命周期是引用的一部分,在运行时没有任何多余的内存开销。 引用的源变量的生命周期应该大过引用的生命周期,引用的生命周期应该大过存储引用的变量的生命周期。假如存储引用的变量的生命周期大过引用的生命周期,那么变量在某个时刻就会变成垂悬指针。 假如一个复杂对象的一部分是引用,那么引用源变量的生命周期应该大过复杂对象的生命周期。比如假如你构建一个引用的向量,那么向量所有元素(引用)的源变量的生命周期都应该大过向量的生命周期。 在rust里等价其他语言的全局变量叫静态变量,静态变量从程序开始存活到程序结束。每个静态变量都必须初始化,可变静态变量是线程不安全的,即使在单线程程序里也会成为其它问题的受害者。基于以上原因,只能在不安全块里使用可变静态变量。 函数fn f(p:&i32)的完整签名写法是fn f<'a>(p:&'a i32)。这里'a是函数f的生命周期参数,读作对任意的生命周期定义函数f。静态引用变量的生命周期是静态生命周期'static。我们不能在函数里把任意生命周期的引用参数存到全局引用变量里,在rust里,函数签名暴露了函数的行为,反过来说,假如函数的引用参数使用了任意生命周期,那么它绝不会存储该参数引用,已经没有必要再查看函数的细节,函数签名就表明了它能对参数做什么,不能对参数做什么。这对安全调用函数来说很有用。 函数参数为任意生命周期的引用时,调用该函数会为参数选择最小的合适的生命周期。 尽管函数有一个生命周期参数,在调用的时候却不用指出,你只需要在定义函数的时候写生命周期就行了,调用的时候rust会为你自动推断。 如果函数只有一个引用参数,并且返回引用类型,rust假定他们拥有相同的生命周期。函数的生命周期参数让rust推断参数引用和返回引用的关系以确保引用被安全地使用。 不管任何时候,引用出现在其他类型的定义里面,你都要写明生命周期参数。一个类型的生命周期参数暴露了它包含引用的生命周期,和这些生命周期的约束。 rust中每种类型都有生命周期,包括i32和String等,大多数情况下是'static,表示它的值可以由你决定活得任意长。 如果结构体的两个成员引用用相同的生命周期,那么rust必须找到一个让两个引用都可用的生命周期,如果找不到会报编译错误,可以让两个引用声明成两个独立的生命周期。 假如你的函数没有返回引用或者包含引用的类型,你不需要写出生命周期参数,rust会自动为每个引用参数指定独立的生命周期。假如参数里只有一个生命周期,那么rust假定返回引用的生命周期和它一样。假如有多个生命周期参数和一个引用类型的返回值,那么rust无法自动推断它们的关系,需要你手动指出。 如果函数是某些类型的方法,那么self不算在里面。rust假定self的生命周期是返回值的生命周期,rust假定不论你的返回值借用的什么,都是从self借用的??? 共享引用让所有者变成已读,在共享引用归还之前不能对所有者执行移动操作(比如赋值给其他变量或者传递给函数) 共享访问是只读访问,在共享引用存在的区间,所有者不能被做任何的更改,也不能同时存在可变引用,所有者被冻结了,是只读的。 可变引用是独占访问的,在可变引用存在期间,所有者不可用,唯一一种能和可变引用重叠生命周期的方法就是从可变引用里再借用。可变引用可以再借用。 数据竞争只有一个值在跨线程既可变又共享的情况才可能,在rust里已经被明确地消除了,不使用unssfe块的rust并发程序是不会出现数据竞争的。 基于引用计数的多所有权模型导致内存结构是网状的,难以测试和剥离模块。rust的单所有权模型内存结构是树状的。 要在rust里创建循环引用结构需要用到智能指针比如Rc类型,和interior mutability。