当前位置: 首页 > news >正文

那里有做网站做外贸网站怎么访问外国网站

那里有做网站,做外贸网站怎么访问外国网站,网站推广有哪些手段,wordpress小标签智能指针 智能指针虽然也号称指针#xff0c;但是它是一个复杂的家伙#xff1a;通过比引用更复杂的数据结构#xff0c;包含比引用更多的信息#xff0c;例如元数据#xff0c;当前长度#xff0c;最大可用长度等。引用和智能指针的另一个不同在于前者仅仅是借用了数据…智能指针 智能指针虽然也号称指针但是它是一个复杂的家伙通过比引用更复杂的数据结构包含比引用更多的信息例如元数据当前长度最大可用长度等。引用和智能指针的另一个不同在于前者仅仅是借用了数据而后者往往可以拥有它们指向的数据然后再为其它人提供服务。智能指针往往是基于结构体实现它与我们自定义的结构体最大的区别在于它实现了 Deref 和 Drop 特征 Deref 可以让智能指针像引用那样工作这样你就可以写出同时支持智能指针和引用的代码例如 *TDrop 允许你指定智能指针超出作用域后自动执行的代码例如做一些数据清除等收尾工作 BoxT 堆对象分配 BoxT 允许你将一个值分配到堆上然后在栈上保留一个智能指针指向堆上的数据。Rust 堆上对象还有一个特殊之处它们都拥有一个所有者因此受所有权规则的限制当赋值时发生的是所有权的转移只需浅拷贝栈上的引用或智能指针即可。 堆栈的性能 很多人可能会觉得栈的性能肯定比堆高其实未必。 小型数据在栈上的分配性能和读取性能都要比堆上高中型数据栈上分配性能高但是读取性能和堆上并无区别因为无法利用寄存器或 CPU 高速缓存最终还是要经过一次内存寻址大型数据只建议在堆上分配和使用 总之栈的分配速度肯定比堆上快但是读取速度往往取决于你的数据能不能放入寄存器或 CPU 高速缓存。 因此不要仅仅因为堆上性能不如栈这个印象就总是优先选择栈导致代码更复杂的实现。 Box的使用场景 由于 Box 是简单的封装除了将值存储在堆上外并没有其它性能上的损耗。而性能和功能往往是鱼和熊掌因此 Box 相比其它智能指针功能较为单一可以在以下场景中使用它 特意的将数据分配在堆上数据较大时又不想在转移所有权时进行数据拷贝类型的大小在编译期无法确定但是我们又需要固定大小的类型时特征对象用于说明对象实现了一个特征而不是某个特定的类型在特征对象的时候我们已经见到过了Box 使用 BoxT 将数据存储在堆上 前面的文章我们提到过标量数据类型是被存储在栈上的。现在我们使用Box来将其存储在堆上。例如 fn main() {let num Box::new(1);println!({}, num);let sum *num 1;println!({sum}); }创建一个智能指针指向了存储在堆上的 1并且 num 持有了该指针。 println! 可以正常打印出 a 的值是因为它隐式地调用了 Deref 对智能指针 a 进行了解引用let sum *num 1需要手动解引用这是因为在表达式中rust无法自动隐式地执行 Deref 解引用操作你需要使用 * 操作符来显式的进行解引用num持有的智能指针将在作用域结束main 函数结束时被释放掉这是因为 BoxT 实现了 Drop 特征 避免栈上数据的拷贝 当栈上数据转移所有权时实际上是把数据拷贝了一份最终新旧变量各自拥有不同的数据因此所有权并未转移。而堆上则不然底层数据并不会被拷贝转移所有权仅仅是复制一份栈中的指针再将新的指针赋予新的变量然后让拥有旧指针的变量失效最终完成了所有权的转移 fn main() {// 在栈上创建一个长度为1000的数组let arr [0;1000];// 将arr所有权转移arr1由于 arr 分配在栈上因此这里实际上是直接重新深拷贝了一份数据let arr1 arr;// arr 和 arr1 都拥有各自的栈上数组因此不会报错println!({:?}, arr.len());println!({:?}, arr1.len());// 在堆上创建一个长度为1000的数组然后使用一个智能指针指向它let arr Box::new([0;1000]);// 将堆上数组的所有权转移给 arr1由于数据在堆上因此仅仅拷贝了智能指针的结构体底层数据并没有被拷贝// 所有权顺利转移给 arr1arr 不再拥有所有权let arr1 arr;println!({:?}, arr1.len());// 由于 arr 不再拥有底层数组的所有权因此下面代码将报错// println!({:?}, arr.len()); }在这种数据较大的时候将数据保存在堆上。在转移所有权的时候代价较小。 使用Box来构建链表节点 struct NodeT {value: OptionT,next: OptionBoxNodeT }链表的节点可能类似于上面和我们在C中使用指针定义链表节点的方式非常类似不过在rust里我们实现一个链表是相当困难的因为我们受到所有权和声明周期的约束。 特征对象 回归一下之前的特征对象他帮助我们实现了某种意义上的鸭子类型。 trait Draw {fn draw(self); }struct Button {id: u32, } impl Draw for Button {fn draw(self) {println!(这是屏幕上第{}号按钮, self.id)} }struct Select {id: u32, }impl Draw for Select {fn draw(self) {println!(这个选择框贼难用{}, self.id)} }fn main() {let elems: VecBoxdyn Draw vec![Box::new(Button { id: 1 }), Box::new(Select { id: 2 })];for e in elems {e.draw()} }以上代码将不同类型的 Button 和 Select 包装成 Draw 特征的特征对象放入一个数组中Boxdyn Draw 就是特征对象。其实特征也是动态大小类型而特征对象在做的就是将动态大小类型转换为固定大小类型。 Box 内存布局 直接参考Rust语言圣经中的讲解。 Box::leak Box 中还提供了一个非常有用的关联函数Box::leak它可以消费掉 Box 并且强制目标值从内存中泄漏。其实还真有点用例如你可以把一个 String 类型变成一个 static 生命周期的 str 类型 fn main() {let s gen_static_str();println!({}, s); }fn gen_static_str() - static str{let mut s String::new();s.push_str(hello, world);Box::leak(s.into_boxed_str()) }在之前的代码中如果 String 创建于函数中那么返回它的唯一方法就是转移所有权给调用者 fn move_str() - String而通过 Box::leak 我们不仅返回了一个 str 字符串切片它还是 static 生命周期的 你需要一个在运行期初始化的值但是可以全局有效也就是和整个程序活得一样久那么就可以使用 Box::leak Deref 解引用 智能指针的名称来源主要就在于它实现了 Deref 和 Drop 特征这两个特征可以智能地帮助我们节省使用上的负担Deref 可以让智能指针像引用那样工作这样你就可以写出同时支持智能指针和引用的代码例如 *T。 常规引用是一个指针类型包含了目标数据存储的内存地址。对常规引用使用 * 操作符就可以通过解引用的方式获取到内存地址对应的数据值 fn main() {let x 5;let y x;assert_eq!(5, x);assert_eq!(5, *y); }这里 y 就是一个常规引用包含了值 5 所在的内存地址然后通过解引用 *y我们获取到了值 5。如果你试图执行 assert_eq!(5, y);代码就会无情报错因为你无法将一个引用与一个数值做比较。 考虑一下智能指针*它是一个结构体类型如果你直接对它进行 myStruct显然编译器不知道该如何办因此我们可以为智能指针结构体实现 Deref 特征。 实现 Deref 后的智能指针结构体就可以像普通引用一样通过 * 进行解引用例如 BoxT 智能指针 fn main() {let x Box::new(1);let sum *x 1; }智能指针 x 被 * 解引用为 i32 类型的值 1然后再进行求和。 *背后的原理 当我们对智能指针 Box 进行解引用时实际上 Rust 为我们调用了以下方法 *(y.deref())首先调用 deref 方法返回值的常规引用然后通过 * 对常规引用进行解引用最终获取到目标值。 至于 Rust 为何要使用这个有点啰嗦的方式实现原因在于所有权系统的存在。如果 deref 方法直接返回一个值而不是引用那么该值的所有权将被转移给调用者而我们不希望调用者仅仅只是 *T 一下就拿走了智能指针中包含的值。 需要注意的是* 不会无限递归替换从 *y 到 *(y.deref()) 只会发生一次而不会继续进行替换然后产生形如 *((y.deref()).deref()) 的怪物。 函数和方法中的隐式 Deref 转换 对于函数和方法的传参Rust 提供了一个极其有用的隐式转换Deref 转换。若一个类型实现了 Deref 特征那它的引用在传给函数或方法时会根据参数签名来决定是否进行隐式的 Deref 转换例如 fn main() {let s String::from(hello world);display(s) }fn display(s: str) {println!({},s); }String 实现了 Deref 特征可以在需要时自动被转换为 str 类型s 是一个 String 类型当它被传给 display 函数时自动通过 Deref 转换成了 str必须使用 s 的方式来触发 Deref(仅引用类型的实参才会触发自动解引用) 连续的隐式 Deref 转换 如果你以为 Deref 仅仅这点作用那就大错特错了。Deref 可以支持连续的隐式转换直到找到适合的形式为止 fn main() {let s MyBox::new(String::from(hello world));display(s) }fn display(s: str) {println!({},s); }这里我们使用了之前自定义的智能指针 MyBox并将其通过连续的隐式转换变成 str 类型首先 MyBox 被 Deref 成 String 类型结果并不能满足 display 函数参数的要求编译器发现 String 还可以继续 Deref 成 str最终成功的匹配了函数参数。 总之当参与其中的类型定义了 Deref 特征时Rust 会分析该类型并且连续使用 Deref 直到最终获得一个引用来匹配函数或者方法的参数类型这种行为完全不会造成任何的性能损耗因为完全是在编译期完成。 但是 Deref 并不是没有缺点缺点就是如果你不知道某个类型是否实现了 Deref 特征那么在看到某段代码时并不能在第一时间反应过来该代码发生了隐式的 Deref 转换。事实上不仅仅是 Deref在 Rust 中还有各种 From/Into 等等会给阅读代码带来一定负担的特征。还是那句话一切选择都是权衡有得必有失得了代码的简洁性往往就失去了可读性Go 语言就是一个刚好相反的例子。 再来看一下在方法、赋值中自动应用 Deref 的例子 fn main() {let s MyBox::new(String::from(hello, world));let s1: str s;let s2: String s.to_string(); }对于 s1我们通过两次 Deref 将 str 类型的值赋给了它在表达式中需要手动解引用而对于 s2我们在其上直接调用方法 to_string实际上 MyBox 根本没有没有实现该方法能调用 to_string完全是因为编译器对 MyBox 应用了 Deref 的结果方法调用会自动解引用。 Deref 规则总结 一个类型为 T 的对象 foo如果 T: DerefTargetU那么相关 foo 的引用 foo 在应用的时候会自动转换为 U。 三种 Deref 转换 在之前我们讲的都是不可变的 Deref 转换实际上 Rust 还支持将一个可变的引用转换成另一个可变的引用以及将一个可变引用转换成不可变的引用规则如下 当 T: DerefTargetU可以将 T 转换成 U也就是我们之前看到的例子当 T: DerefMutTargetU可以将 mut T 转换成 mut U当 T: DerefTargetU可以将 mut T 转换成 U 来看一个关于 DerefMut 的例子 struct MyBoxT {v: T, }implT MyBoxT {fn new(x: T) - MyBoxT {MyBox { v: x }} }use std::ops::Deref;implT Deref for MyBoxT {type Target T;fn deref(self) - Self::Target {self.v} }use std::ops::DerefMut;implT DerefMut for MyBoxT {fn deref_mut(mut self) - mut Self::Target {mut self.v} }fn main() {let mut s MyBox::new(String::from(hello, ));display(mut s) }fn display(s: mut String) {s.push_str(world);println!({}, s); }以上代码有几点值得注意: 要实现 DerefMut 必须要先实现 Deref 特征pub trait DerefMut: Deref T: DerefMutTargetU 解读将 mut T 类型通过 DerefMut 特征的方法转换为 mut U 类型对应上例中就是将 mut MyBoxString 转换为 mut String 对于上述三条规则中的第三条它比另外两条稍微复杂了点Rust 可以把可变引用隐式的转换成不可变引用但反之则不行。 如果从 Rust 的所有权和借用规则的角度考虑当你拥有一个可变的引用那该引用肯定是对应数据的唯一借用那么此时将可变引用变成不可变引用并不会破坏借用规则但是如果你拥有一个不可变引用那同时可能还存在其它几个不可变的引用如果此时将其中一个不可变引用转换成可变引用就变成了可变引用与不可变引用的共存最终破坏了借用规则。 Drop 释放资源 指定在值离开作用域时应该执行的代码的方式是实现 Drop trait。Drop trait 要求实现一个叫做 drop 的方法它获取一个 self 的可变引用。 struct HasDrop1; struct HasDrop2; impl Drop for HasDrop1 {fn drop(mut self) {println!(Dropping HasDrop1!);} } impl Drop for HasDrop2 {fn drop(mut self) {println!(Dropping HasDrop2!);} } #[allow(unused)] struct HasTwoDrops {one: HasDrop1,two: HasDrop2, } impl Drop for HasTwoDrops {fn drop(mut self) {println!(Dropping HasTwoDrops!);} }struct Foo;impl Drop for Foo {fn drop(mut self) {println!(Dropping Foo!)} }fn main() {let _x HasTwoDrops {one: HasDrop1,two: HasDrop2,};let _foo Foo;println!(Running!); }这段程序的执行结果如下所示 Running! Dropping Foo! Dropping HasTwoDrops! Dropping HasDrop1! Dropping HasDrop2!这段代码中 Drop 特征中的 drop 方法借用了目标的可变引用而不是拿走了所有权。结构体中每个字段都有自己的 Drop Drop 的顺序 观察以上输出我们可以得出以下关于 Drop 顺序的结论 变量级别按照逆序的方式(入栈出栈)_x 在 _foo 之前创建因此 _x 在 _foo 之后被 drop结构体内部按照字段定义顺序的方式结构体 _x 中的字段按照定义中的顺序依次 drop 没有实现 Drop 的结构体 实际上就算你不为 _x 结构体实现 Drop 特征它内部的两个字段依然会调用 drop我们可以移除HasTwoDrops的Drop trait。原因在于Rust 自动为几乎所有类型都实现了 Drop 特征因此就算你不手动为结构体实现 Drop它依然会调用默认实现的 drop 函数同时再调用每个字段的 drop 方法。输出结果如下所示 Running! Dropping Foo! Dropping HasDrop1! Dropping HasDrop2!Drop 使用场景 对于 Drop 而言主要有两个功能 回收内存资源执行一些收尾工作 对于第二点在之前我们已经详细介绍过因此这里主要对第一点进行下简单说明。 在绝大多数情况下我们都无需手动去 drop 以回收内存资源因为 Rust 会自动帮我们完成这些工作它甚至会对复杂类型的每个字段都单独的调用 drop 进行回收但是确实有极少数情况需要你自己来回收资源的例如文件描述符、网络 socket 等当这些值超出作用域不再使用时就需要进行关闭以释放相关的资源在这些情况下就需要使用者自己来解决 Drop 的问题。 互斥的 Copy 和 Drop 我们无法为一个类型同时实现 Copy 和 Drop 特征。因为实现了 Copy 的特征会被编译器隐式的复制因此非常难以预测析构函数执行。因此这些实现了 Copy 的类型无法拥有析构函数。 #[derive(Copy)] struct Foo;impl Drop for Foo {fn drop(mut self) {println!(Dropping Foo!)} }这段代码会报错告诉我们在具有析构函数的结构体上无法实现Copy trait。 the trait Copy may not be implemented for this type; the type has a destructor自定义智能指针 如前所述我们需要实现智能指针那么只需要实现Deref trait和Drop trait即可。 use std::ops::Deref;struct MyBoxT (T);implT MyBoxT {fn new(v: T) - Self {MyBox(v)} }implT Deref for MyBoxT{type Target T;fn deref(self) - Self::Target {self.0} }implT Drop for MyBoxT{fn drop(mut self) {println!(Drop)} }fn my_print(v: i32) {println!({}, v); }fn main() {let x MyBox::new(123);my_print(x); // 当引用在传给函数或方法时自动进行隐式deref调用。 }我们为MyBox实现了Drop trait和 Deref trait从而让MyBox变为智能指针。我们的drop接口方法实际上什么都没干只是打印了Drop。在实际自定义智能指针的时候几乎是不需要实现Drop trait的因为rust 自动为几乎所有类型都实现了 Drop 特征。 Rc与Arc Rust 所有权机制要求一个值只能有一个所有者在大多数情况下都没有问题但是考虑以下情况 在图数据结构中多个边可能会拥有同一个节点该节点直到没有边指向它时才应该被释放清理在多线程中多个线程可能会持有同一个数据但是你受限于 Rust 的安全机制无法同时获取该数据的可变引用 以上场景不是很常见但是一旦遇到就非常棘手为了解决此类问题Rust 在所有权机制之外又引入了额外的措施来简化相应的实现通过引用计数的方式允许一个数据资源在同一时刻拥有多个所有者。 这种实现机制就是 Rc 和 Arc前者适用于单线程后者适用于多线程。 RcT 引用计数(reference counting)顾名思义通过记录一个数据被引用的次数来确定该数据是否正在被使用。当引用次数归零时就代表该数据不再被使用因此可以被清理释放。 当我们希望在堆上分配一个对象供程序的多个部分使用且无法确定哪个部分最后一个结束时就可以使用 Rc 成为数据值的所有者。 下面是经典的所有权被转移导致报错的例子 fn main() {let s String::from(hello, world);// s在这里被转移给alet a Box::new(s);// 报错此处继续尝试将 s 转移给 blet b Box::new(s); }使用 Rc 就可以轻易解决 use std::rc::Rc; fn main() {let a Rc::new(String::from(hello, world));let b Rc::clone(a);assert_eq!(2, Rc::strong_count(a));assert_eq!(Rc::strong_count(a), Rc::strong_count(b)) }以上代码我们使用 Rc::new 创建了一个新的 RcString 智能指针并赋给变量 a该指针指向底层的字符串数据。智能指针 RcT 在创建时还会将引用计数加 1此时获取引用计数的关联函数 Rc::strong_count 返回的值将是 1。 接着我们又使用 Rc::clone 克隆了一份智能指针 RcString并将该智能指针的引用计数增加到 2。由于 a 和 b 是同一个智能指针的两个副本因此通过它们两个获取引用计数的结果都是 2。 这里的 clone 仅仅复制了智能指针并增加了引用计数并没有克隆底层数据因此 a 和 b 是共享了底层的字符串 s不是所有的clone都会进行深拷贝。下面的例子展示了引用计数的变化。 use std::rc::Rc; fn main() {let a Rc::new(String::from(test ref counting));println!(count_a {}, Rc::strong_count(a));let b Rc::clone(a);println!(count_a {}, count_b {}, Rc::strong_count(a), Rc::strong_count(b));{let c Rc::clone(a);println!(count_a {}, count_b {}, count_c {}, Rc::strong_count(a), Rc::strong_count(b), Rc::strong_count(c));}println!(After:);println!(count_a {}, count_b {}, Rc::strong_count(a), Rc::strong_count(b)); }由于变量 c 在语句块内部声明当离开语句块时它会因为超出作用域而被释放所以引用计数会减少 1事实上这个得益于 RcT 实现了 Drop 特征。a、b、c 三个智能指针引用计数都是同样的并且共享底层的数据因此打印计数时用哪个都行这里选择全部打印是为了直观展示效果。当 a、b 超出作用域后引用计数会变成 0最终智能指针和它指向的底层字符串都会被清理释放。 事实上RcT 是指向底层数据的不可变的引用因此你无法通过它来修改数据这也符合 Rust 的借用规则要么存在多个不可变借用要么只能存在一个可变借用。如果需要修改数据那么在rust中使用Arc 跟 Mutex 锁的组合非常常见它们既可以让我们在不同的线程中共享数据又允许在各个线程中对其进行修改。 在多线程中使用 RcT use std::rc::Rc; use std::thread;fn main() {let s Rc::new(String::from(多线程漫游者));for _ in 0..10 {let s Rc::clone(s);let handle thread::spawn(move || { // ERROR, RcString cannot be sent between threads safelyprintln!({}, s)});} }spawn的参数是一个闭包并且使用move将s的所有权转移到闭包中。而spawn会开启一个线程那么意味着s的所有权转移到一个新的线程中。但是上述代码会报错原因是 RcT 不能在线程间安全的传递实际上是因为它没有实现 Send 特征而该特征是恰恰是多线程间传递数据的关键我们会在多线程章节中进行讲解。当然还有更深层的原因由于 RcT 需要管理引用计数但是该计数器并没有使用任何并发原语因此无法实现原子化的计数操作最终会导致计数错误。 Arc Arc 是 Atomic Rc 的缩写顾名思义原子化的 RcT 智能指针。它能保证我们的数据能够安全的在线程间共享. Arc 的性能损耗 原子化或者其它锁虽然可以带来的线程安全但是都会伴随着性能损耗而且这种性能损耗还不小。因此 Rust 把这种选择权交给我们自己。对比Python语言它的标准实现CPython是线程安全的Python使用全局解释器锁(GIL)来确保同一时刻只有一个线程在执行Python字节码。 Arc 和 Rc 拥有完全一样的 API将上面报错的代码从Rc改成Arc。 use core::time; use std::sync::Arc; use std::thread::{self, sleep};#[allow(unused)] fn main() {let s Arc::new(String::from(多线程漫游者));for _ in 0..10 {let s Arc::clone(s);let handle thread::spawn(move || {println!({}, s)});}sleep(time::Duration::from_secs(3)); // 加了三秒等待所有线程执行完毕。否则输出几个“多线程漫游者是不确定的”。 }Arc 和 Rc 并没有定义在同一个模块前者通过 use std::sync::Arc 来引入后者通过 use std::rc::Rc。大家可以去掉最后一行多次执行代码看看效果。 Rc和Arc简单总结 Rc/Arc 是不可变引用你无法修改它指向的值只能进行读取。一旦最后一个拥有者消失则资源会自动被回收这个生命周期是在编译期就确定下来的Rc 只能用于同一线程内部想要用于线程之间的对象共享你需要使用 ArcRcT/ArcT 是一个智能指针实现了 Deref 特征因此你无需先解开 Rc/Arc 指针再使用里面的 T而是可以直接使用 T Rc 和 Arc 的区别在于后者是原子化实现的引用计数因此是线程安全的可以用于多线程中共享数据。这两者都是只读的如果想要实现内部数据可修改必须配合内部可变性 RefCell 或者互斥锁 Mutex 来一起使用。 Cell 和 RefCell OK我们终于来到了可以在拥有不可变引用的同时修改目标数据。之前的Rc只是让我们在同一线程内通过引用计数的方式允许一个数据资源在同一时刻拥有多个所有者而Arc也只不过是在Rc的基础上扩展到了多线程。我们仍旧无法修改数据只能传递数据。 为此 Rust 提供了 Cell 和 RefCell 用于内部可变性。 内部可变性的实现是因为 Rust 使用了 unsafe 来做到这一点但是对于使用者来说这些都是透明的因为这些不安全代码都被封装到了安全的 API 中。 Cell Cell 和 RefCell 在功能上没有区别区别在于 CellT 适用于 T 实现 Copy 的情况。例如 use std::cell::Cell; fn main() {let c Cell::new(123);let one c.get();c.set(456);let two c.get();println!({}, {}, one, two); }“123” 是 str 类型它实现了 Copy 特征c.get 用来取值c.set 用来设置新值 取到值保存在 one 变量后还能同时进行修改这个违背了 Rust 的借用规则但是由于 Cell 的存在我们很优雅地做到了这一点但是如果你尝试在 Cell 中存放String编译器会立刻报错因为 String 没有实现 Copy 特征 RefCell 由于 Cell 类型针对的是实现了 Copy 特征的值类型因此在实际开发中Cell 使用的并不多因为我们要解决的往往是可变、不可变引用共存导致的问题此时就需要借助于 RefCell 来达成目的。 use std::cell::RefCell;fn main() {let s RefCell::new(String::from(hello, world));let s1 s.borrow(); // 不可变引用let s2 s.borrow_mut(); // 可变引用println!({},{}, s1, s2); }这段代码在编译的时候不会报错但是在运行时会报错。RefCell可以使用.borrow()方法来获取引用并使用.borrow_mut()方法来获取可变引用。当RefCell离开其作用域时会自动检查是否存在任何悬空引用如果存在则会引发panic。RefCell不适合多线程并发访问因为它不是线程安全的。对于多线程访问Rust的std::sync::Mutex和std::sync::RwLock提供了更好的选择。 RefCell 实际上并没有解决可变引用和引用可以共存的问题只是将报错从编译期推迟到运行时从编译器错误变成了 panic 异常。 RefCell 为何存在 Rust 编译期的宁可错杀绝不放过的原则当编译器不能确定你的代码是否正确时就统统会判定为错误因此难免会导致一些误报。而 RefCell 正是用于你确信代码是正确的而编译器却发生了误判时。 有时候你可能很难管理可变和不可变因此你确实需要RefCell帮你通过编译。总之当你确信编译器误报但不知道该如何解决时或者你有一个引用类型需要被四处使用和修改然后导致借用关系难以管理时都可以优先考虑使用 RefCell。 RefCell 简单总结 与 Cell 用于可 Copy 的值不同RefCell 用于引用RefCell 只是将借用规则从编译期推迟到程序运行期并不能帮你绕过这个规则RefCell 适用于编译期误报或者一个引用被在多处代码使用、修改以至于难于管理借用关系时使用 RefCell 时违背借用规则会导致运行期的 panic 选择 Cell 还是 RefCell 根据本文的内容我们可以大概总结下两者的区别 Cell 只适用于 Copy 类型用于提供值而 RefCell 用于提供引用Cell 不会 panic而 RefCell 会 性能比较 Cell 没有额外的性能损耗例如以下两段代码的性能其实是一致的 // code snipet 1 let x Cell::new(1); let y x; let z x; x.set(2); y.set(3); z.set(4); println!({}, x.get());// code snipet 2 let mut x 1; let y mut x; let z mut x; x 2; *y 3; *z 4; println!({}, x);虽然性能一致但代码 1 拥有代码 2 不具有的优势它能编译成功 而RefCell使用了运行时借用检查每次使用.borrow()或.borrow_mut()方法时都会进行借用检查这会影响性能。总之当非要使用内部可变性时首选 Cell只有你的类型没有实现 Copy 时才去选择 RefCell。 内部可变性 之前我们提到 RefCell 具有内部可变性何为内部可变性简单来说对一个不可变的值进行可变借用就是内部可变性。阅读下面的代码。 use std::cell::RefCell; pub trait Messenger {fn send(self, msg: String); }pub struct MsgQueue {msg_cache: RefCellVecString, }impl Messenger for MsgQueue {fn send(self, msg: String) {self.msg_cache.borrow_mut().push(msg)} }fn main() {let mq MsgQueue {msg_cache: RefCell::new(Vec::new()),};mq.send(hello, world.to_string()); }mq是MsgQueue的不可变实例因此我们无法通过常规手段修改mq中的msg_cache字段。此时在确保代码正确的情况下我们可以使用RefCell来改变不可变的mq中的msg_cache。 结构体中的字段可变性取决于结构体对象本身是否是可变的上述例子中的mq是不可变的因此msg_cache字段也是不可变的。而我们通过使用RefCell来改变了msg_cache字段。这种内部可变性提供给了我们这种可能。 参考资料 Rust语言圣经(Rust Course)Rust 程序设计语言
http://www.w-s-a.com/news/39464/

相关文章:

  • 网站 免费 托管运营app软件大全
  • 爱网站找不到了网站设计制作要交印花税
  • 分销平台是什么意思网站如何从行为数据进行优化
  • 做网站公司职务做民俗酒店到哪些网站推荐
  • 从0到建网站wordpress导航主题模板下载地址
  • 以3d全景做的网站统计网站的代码
  • 北辰网站建设WordPress换主题文件夹
  • 做网站的合同范文百度分析工具
  • 深圳企业网站制作公司单位注册wordpress发送邮件
  • 兰州专业网站建设团队wordpress 拉取点击数
  • 基于php房产网站开发ppt模板免费下载第一ppt
  • 网站盈利模式分析怎么做山东营销网站建设联系方式
  • 二级网站建设 知乎我的个人主页模板
  • wordpress小说网站模板下载地址百度优化服务
  • 云南网页设计制作seo计费系统源码
  • 屏蔽ip网站吗行业外贸网站建设
  • 河北城乡建设学校网站常州网站建设公司平台
  • 合肥网站建设市场分析网站收录后怎么做排名
  • 湖南企业网站建设如何推广手机网站
  • 网站建设项目经历网站推广服务 商务服务
  • 加强网站的建设福州seo排名外包
  • 做婚庆找什么网站有专门为个人网站做推广的吗
  • 网站搭建要求模板学编程需要英语基础吗
  • 网上如何建网站卖量具净水机企业网站源码
  • 网站推广 软件规划设计公司年终总结
  • 视频网站开发方法微站网建站系统
  • 渐变网站网页界面设计的宗旨是什么
  • 网站排名提升工具免费韶关做网站公司
  • 做网站一个月可以赚多少钱东营市建设工程招标网
  • 网站开发工具阿里云怎么做网站