您的位置:首页 > 新闻 > 热点要闻 > 前端低代码开发_dw制作一张完整网页_抄一则新闻四年级_谷歌seo关键词优化

前端低代码开发_dw制作一张完整网页_抄一则新闻四年级_谷歌seo关键词优化

2024/12/29 3:21:22 来源:https://blog.csdn.net/neweastsun/article/details/143629585  浏览:    关键词:前端低代码开发_dw制作一张完整网页_抄一则新闻四年级_谷歌seo关键词优化
前端低代码开发_dw制作一张完整网页_抄一则新闻四年级_谷歌seo关键词优化

本文试图解释初学者都会遇到的一个概念Rust:它的“借用检查机制”。借用检查机制检查所有对数据的访问是否合法。检查所有的数据访问是否合法,可以让Rust避免安全问题。通过避免与编译器发生冲突,了解这个系统的工作原理至少可以加快开发时间。更重要的是,学会使用借用检查机制可以让你自信地构建更大的软件系统。

引出问题

为了简化,我们的示例场景为:通过机器ID查询其状态,检查方法始终返回相同的值。

  • 示例代码:
fn main() {let mac_a_id = 1;let mac_b_id = 2;let a_status = show_status(mac_a_id);let b_status = show_status(mac_b_id);println!("a:{:?}, b:{:?}", a_status, b_status);let a_status = show_status(mac_a_id);let b_status = show_status(mac_b_id);println!("a:{:?}, b:{:?}", a_status, b_status);
}#[derive(Debug)]
enum MacStatus {OK,
}fn show_status(mac_id: u32) -> MacStatus{MacStatus::OK
}

程序编译正常,输出结果;

a:OK, b:OK
a:OK, b:OK

下面我们定义结构体Machine,为了简化仅包括id属性。

  • 示例代码:
fn main() {let mac_a_id = Machine{id:1};let mac_b_id = Machine{id:2};let a_status = show_status(mac_a_id);let b_status = show_status(mac_b_id);println!("a:{:?}, b:{:?}", a_status, b_status);let a_status = show_status(mac_a_id);let b_status = show_status(mac_b_id);println!("a:{:?}, b:{:?}", a_status, b_status);
}#[derive(Debug)]
enum MacStatus {OK,
}#[derive(Debug)]
struct Machine{id: u32,
}fn show_status(mac_id: Machine) -> MacStatus{MacStatus::OK
}

编译程序报错:

error[E0382]: use of moved value: `mac_a_id`--> src/main.rs:10:32|
2  |     let mac_a_id = Machine{id:1};|         -------- move occurs because `mac_a_id` has type `Machine`, which does not implement the `Copy` trait
...
5  |     let a_status = show_status(mac_a_id);|                                -------- value moved here
...
10 |     let a_status = show_status(mac_a_id);|                                ^^^^^^^^ value used here after move|
note: consider changing this parameter type in function `show_status` to borrow instead if owning the value isn't necessary--> src/main.rs:26:24...

通过上面错误信息可以看到:move occurs because mac_a_idhas typeMachine, which does not implement the Copy; mac_a_id 是 Machine类型,在第二次调用 show_status(mac_a_id); 时报错了,原因是没有实现Copy特征,编译器给的建议是改变 show_status` 函数参数类型,如果不是必须要所有权,使用借用(borrow)代替。

这时你可能疑惑,为啥前面的示例没有错误。原因是前面mac_a_id变量采用基础类型,基础类型默认都实现了Copy特性。Machine是自定义类型,默认没有实现Copy特性。由于在赋值和函数调用场景中,会发生所有权转移,因此第二次调用时mac_a_id已失效,不能再次被使用了。

  • 解决所有权问题

Rust 的所有权系统非常出色。它提供了无需垃圾回收器即可实现内存安全的途径。但是,有一个“但是”。如果你不了解正在发生的事情,所有权系统可能会让你更加迷茫。特别是当你将过去的编程风格应用到新的范式时。

以下四种策略可以帮助解决所有权问题:

  1. 在不需要完全所有权的地方使用引用
  2. 对于复制成本可以接受时,使用复制
  3. 使用包装器帮助处理共享数据所有权

使用引用

修改show_status函数,参数使用引用传递:

fn main() {let mac_a_id = Machine{id:1};let mac_b_id = Machine{id:2};let a_status = show_status(&mac_a_id);let b_status = show_status(&mac_b_id);println!("a:{:?}, b:{:?}", a_status, b_status);let a_status = show_status(&mac_a_id);let b_status = show_status(&mac_b_id);println!("a:{:?}, b:{:?}", a_status, b_status);
}#[derive(Debug)]
enum MacStatus {OK,
}#[derive(Debug)]
struct Machine{id: u32,
}fn show_status(mac_id: &Machine) -> MacStatus{MacStatus::OK
}

输出结果一致。show_status(mac_id: &Machine),者意味着仅访问对象,不拥有所有权。

使用复制

每个对象都有一个所有者,这意味着需要对软件进行重大的预先规划和/或重构。正如我们在前一节中所看到的,要摆脱早期的设计决策可能需要做大量的工作。

重构的一种替代方法是简单地复制值。这样做通常是不受欢迎的,但在紧要关头却很有用。基本类型,如整数,就是一个很好的例子。对于CPU来说,复制基本类型的成本很低。事实上,它们是如此便宜,以至于Rust总是复制它们,否则它会担心所有权被转移。

类型可以选择两种复制模式:克隆和复制。当所有权被移动时,复制就会隐式地起作用。对象obj_a的按位被复制以创建对象obj_b。Clone显式地起作用,实现Clone的类型有 obj_a.Clone() 方法,允许执行创建新类型所需的任何操作。

fn main() {let mac_a_id = Machine{id:1};let mac_b_id = Machine{id:2};let a_status = show_status(mac_a_id);let b_status = show_status(mac_b_id);println!("a:{:?}, b:{:?}", a_status, b_status);let a_status = show_status(mac_a_id);let b_status = show_status(mac_b_id);println!("a:{:?}, b:{:?}", a_status, b_status);
}#[derive(Debug)]
enum MacStatus {OK,
}#[derive(Clone, Copy, Debug)]
struct Machine{id: u32,
}fn show_status(mac_id: Machine) -> MacStatus{MacStatus::OK
}

要使 #[derive(Clone, Copy, Debug)] 起作用,结构体或枚举的所有成员都必须已实现了Copy。如果其中包括集合类型(如vec,大小不确定)这将不起作用,当然这是我们可以手动实现Copy和Clone。

impl Copy for Machine { }impl Clone for Machine { fn clone(&self) -> Self {CubeSat { id: self. id } }
}

当数据内容暂用内存较大,复制过程增加资源成本,这时采用引用会比复制更佳。

包装数据

  • & 引用
    • 当你只是想在不获取所有权的情况下访问一个值,并且不需要共享所有权时,使用普通引用。例如,在函数调用中传递参数,只是为了读取数据而不改变数据的所有权和内容,就可以使用 & 引用。
    • 当你需要遵循严格的可变和不可变引用规则,在一个有限的范围内修改数据,并且不涉及共享所有权的情况,也可以使用 &mut 引用。
  • Rc 引用计数智能指针
    • 当你需要在多个部分的代码中共享同一份数据的所有权,并且这些部分的代码生命周期可能不同时,Rc 是很有用的。例如,在一个复杂的数据结构中,多个节点可能需要共享同一个配置值,使用 Rc 可以方便地实现共享而不用担心所有权的转移和数据的过早释放。
    • 不过要注意,由于 Rc 只提供了不可变共享访问,在需要修改共享数据的场景下,需要结合内部可变性机制,并且要谨慎处理可能出现的运行时错误,比如多个地方同时尝试修改数据导致的借用检查失败(如果使用 RefCell)。

但有时变量是结构体的一部分,可能无法克隆该结构体。或者字符串真的很长,你不想克隆它。这是使用Rc的一些原因,它允许您拥有多个所有者。Rc就像一个优秀的办公室职员:Rc记录下谁拥有所有权,多少人拥有所有权。一旦所有者的数量降到0,这个变量就可以消失了。

下面是如何使用Rc。首先想象两个结构体:一个叫City,另一个叫CityData。City有一个城市的信息,而CityData把所有的城市放在一起。

#[derive(Debug)]
struct City {name: String,population: u32,city_history: String,
}#[derive(Debug)]
struct CityData {names: Vec<String>,histories: Vec<String>,
}fn main() {let calgary = City {name: "Calgary".to_string(),population: 1_200_000,// Pretend that this string is very very longcity_history: "Calgary began as a fort called Fort Calgary that...".to_string(),};let canada_cities = CityData {names: vec![calgary.name], // This is using calgary.name, which is shorthistories: vec![calgary.city_history], // But this String is very long};println!("Calgary's history is: {}", calgary.city_history);  // ⚠️
}

当然上面代码不起作用,因为canada_cities现在拥有数据,而calgary没有。出错信息如下:

error[E0382]: borrow of moved value: `calgary.city_history`--> src\main.rs:27:42|
24 |         histories: vec![calgary.city_history], // But this String is very long|                         -------------------- value moved here
...
27 |     println!("Calgary's history is: {}", calgary.city_history);  // ⚠️|                                          ^^^^^^^^^^^^^^^^^^^^ value borrowed here after move|= note: move occurs because `calgary.city_history` has type `std::string::String`, which does not implement the `Copy` trait

我们可以克隆名称:names: vec![calgary.name.clone()],但我们不想克隆city_history,因为它太长了。我们可以用Rc。

use std::rc::Rc;#[derive(Debug)]
struct City {name: String,population: u32,city_history: Rc<String>,
}#[derive(Debug)]
struct CityData {names: Vec<String>,histories: Vec<Rc<String>>,
}fn main() {}

要添加新引用,必须克隆Rc。但是等等,我们不是想要避免使用.clone()吗?不完全是:我们不想克隆整个String。但是Rc的克隆只是复制指针——它基本上是免费的。这就像在一盒书上贴上一张贴纸来表明它是两个人的,而不是做一个全新的盒子。

你可以使用item.clone()或Rc::clone(&item)克隆名为item的Rc。所以calgary.city_history有2个所有者。我们可以使用Rc::strong_count(&item)来检查所有者的数量。同时,让我们添加一个新的所有者。现在我们的代码看起来像这样:

use std::rc::Rc;#[derive(Debug)]
struct City {name: String,population: u32,city_history: Rc<String>, // String inside an Rc
}#[derive(Debug)]
struct CityData {names: Vec<String>,histories: Vec<Rc<String>>, // A Vec of Strings inside Rcs
}fn main() {let calgary = City {name: "Calgary".to_string(),population: 1_200_000,// Pretend that this string is very very longcity_history: Rc::new("Calgary began as a fort called Fort Calgary that...".to_string()), // Rc::new() to make the Rc};let canada_cities = CityData {names: vec![calgary.name],histories: vec![calgary.city_history.clone()], // .clone() to increase the count};println!("Calgary's history is: {}", calgary.city_history);println!("{}", Rc::strong_count(&calgary.city_history));let new_owner = calgary.city_history.clone();
}

这打印2。new_owner现在是Rc。现在如果我们使用println!(“{}”,Rc: strong_count (&calgary.city_history));,我们得到3。

RcReference Counting,引用计数)是一种强指针类型。它通过在内部维护一个引用计数来确保只要还有引用(Rc 指针)指向一个对象,这个对象就不会被销毁。与 Rc 相对的是 Weak 指针(通常与 Rc 一起使用)。Weak 指针不会影响对象的引用计数,它允许对对象进行临时的、非所有权的访问。

弱指针很有用,因为如果两个Rc互相指向对方,它们就不会死亡。这被称为“循环引用”。如果第1项与第2项之间有Rc,第2项与第1项之间也有Rc,它们不可能等于0。在这种情况下,可以使用弱引用。然后Rc将计算引用,但如果它只有弱引用,那么它可能会死亡。使用 Rc::downgrade(&item) 而不是Rc::clone(&item)来创建弱引用。另外,您可以使用 Rc::weak_count(&item) 来查看弱计数。

Rc不允许修改,为此需要包装包装器。Rc<RefCell>是可以用来执行内部可变性的类型,具有内部可变性的对象在修改内部值时呈现不可变的外观。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com