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

建设银行 杭州招聘网站人才招聘网站怎么做

建设银行 杭州招聘网站,人才招聘网站怎么做,农村建设网站,简易个人博客网站源码rust学习 String类型clone和copy结构体的内存分布for循环#xff08;font color redimportant#xff01;#xff09;堆和栈数据结构vector panic失败就 panic: unwrap 和 expect传播错误 模式匹配忽略模式的值绑定 泛型特征Trait定义特征为类型实现特征孤儿规则使… rust学习 String类型clone和copy结构体的内存分布for循环font color redimportant堆和栈数据结构vector panic失败就 panic: unwrap 和 expect传播错误 模式匹配忽略模式的值绑定 泛型特征Trait定义特征为类型实现特征孤儿规则使用特征作为函数参数特征约束trait bound函数返回中的impl Trait自定义类型打印输出关联类型 方法和关联函数线程学习1.多线程的风险2.使用spawn创建线程等待子线程结束move 关键字强制闭包获取其使用的环境值的所有权 OkLet’s rust ! String类型 String 类型 let mut s String::from(hello world);String类型 let mut s1 s字符串切片类型是str let s String::from(hello); let len s.len();let slice s[0..len]; let slice s[..];clone和copy 参考链接!link 结构体的内存分布 for循环important 1..5 代表 1 2 3 4 5 1..5 代表1 2 3 4for in 结构能以几种方式与 Iterator 互动。在 迭代器 trait 一节将会谈到如果没有特别指定for 循环会对给出的集合应用 into_iter 函数把它转换成一个迭代器。这并不是把集合变成迭代器的唯一方法其他的方法有 iter 和iter_mut 函数。 下列三种情况刚好对应上面的表格 iter 在每次迭代中借用集合中的一个元素。这样集合本身不会被改变循环之后仍可以使用。 fn main() {let names vec![Bob, Frank, Ferris];for name in names.iter() {match name {Ferris println!(There is a rustacean among us!),_ println!(Hello {}, name),}} }info_iter 会消耗集合。在每次迭代中集合中的数据本身会被提供。一旦集合被消耗了之后就无法再使用了因为它已经在循环中被 “移除”move了。 fn main() {let names vec![Bob, Frank, Ferris];for name in names.into_iter() {match name {Ferris println!(There is a rustacean among us!),_ println!(Hello {}, name),}} } iter_mut 可变地mutably借用集合中的每个元素从而允许集合被就地修改。 fn main() {let mut names vec![Bob, Frank, Ferris];for name in names.iter_mut() {*name match name {mut Ferris There is a rustacean among us!,_ Hello,}}println!(names: {:?}, names); } 堆和栈 在很多语言中你并不需要经常考虑到栈与堆。不过在像 Rust 这样的系统编程语言中值是位于栈上还是堆上在更大程度上影响了语言的行为以及为何必须做出这样的抉择。我们会在本章的稍后部分描述所有权与栈和堆相关的内容所以这里只是一个用来预热的简要解释。 栈和堆都是代码在运行时可供使用的内存但是它们的结构不同。栈以放入值的顺序存储值并以相反顺序取出值。这也被称作 后进先出last in, first out。想象一下一叠盘子当增加更多盘子时把它们放在盘子堆的顶部当需要盘子时也从顶部拿走。不能从中间也不能从底部增加或拿走盘子增加数据叫做 进栈pushing onto the stack而移出数据叫做 出栈popping off the stack。栈中的所有数据都必须占用已知且固定的大小。在编译时大小未知或大小可能变化的数据要改为存储在堆上。 堆是缺乏组织的当向堆放入数据时你要请求一定大小的空间。内存分配器memory allocator在堆的某处找到一块足够大的空位把它标记为已使用并返回一个表示该位置地址的 指针pointer。这个过程称作 在堆上分配内存allocating on the heap有时简称为 “分配”allocating。将数据推入栈中并不被认为是分配。因为指向放入堆中数据的指针是已知的并且大小是固定的你可以将该指针存储在栈上不过当需要实际数据时必须访问指针。想象一下去餐馆就座吃饭。当进入时你说明有几个人餐馆员工会找到一个够大的空桌子并领你们过去。如果有人来迟了他们也可以通过询问来找到你们坐在哪。 入栈比在堆上分配内存要快因为入栈时分配器无需为存储新数据去搜索内存空间其位置总是在栈顶。相比之下在堆上分配内存则需要更多的工作这是因为分配器必须首先找到一块足够存放数据的内存空间并接着做一些记录为下一次分配做准备。 访问堆上的数据比访问栈上的数据慢因为必须通过指针来访问。现代处理器在内存中跳转越少就越快缓存。继续类比假设有一个服务员在餐厅里处理多个桌子的点菜。在一个桌子报完所有菜后再移动到下一个桌子是最有效率的。从桌子 A 听一个菜接着桌子 B 听一个菜然后再桌子 A然后再桌子 B 这样的流程会更加缓慢。出于同样原因处理器在处理的数据彼此较近的时候比如在栈上比较远的时候比如可能在堆上能更好的工作。 当你的代码调用一个函数时传递给函数的值包括可能指向堆上数据的指针和函数的局部变量被压入栈中。当函数结束时这些值被移出栈。 跟踪哪部分代码正在使用堆上的哪些数据最大限度的减少堆上的重复数据的数量以及清理堆上不再使用的数据确保不会耗尽空间这些问题正是所有权系统要处理的。一旦理解了所有权你就不需要经常考虑栈和堆了不过明白了所有权的主要目的就是为了管理堆数据能够帮助解释为什么所有权要以这种方式工作。 fn main() {let mut x 5;let mut y x;x 6;println!({},{}, x, y) } 输出6,5 为什么y不是6呢因为普通i32变量在赋值的时候只是值的拷贝实际上他们的两个不同的5数据结构 vector 使用宏自动推断类型 fn main() {let v vec![1, 2, 3]; }更新 fn main() {let mut v Vec::new();v.push(5);v.push(6);v.push(7);v.push(8); } panic vscode默认打开的是powershell 查看更多运行错误信息的命令$env:RUST_BACKTRACE1;cargo run失败就 panic: unwrap 和 expect use std::fs::File;fn main() {let f File::open(hello.txt).unwrap(); }use std::fs::File;fn main() {let f File::open(hello.txt).expect(Failed to open hello.txt); }expect 跟 unwrap 很像也是遇到错误直接 panic, 但是会带上自定义的错误提示信息相当于重载了错误打印的函数 传播错误 一般的错误 use std::fs::File; use std::io::{self, Read};fn read_username_from_file() - ResultString, io::Error {// 打开文件f是Result文件句柄,io::Errorlet f File::open(hello.txt);let mut f match f {// 打开文件成功将file句柄赋值给fOk(file) file,// 打开文件失败将错误返回(向上传播)Err(e) return Err(e),};// 创建动态字符串slet mut s String::new();// 从f文件句柄读取数据并写入s中match f.read_to_string(mut s) {// 读取成功返回Ok封装的字符串Ok(_) Ok(s),// 将错误向上传播Err(e) Err(e),} }传播界的大明星 ? use std::fs::File; use std::io; use std::io::Read;fn read_username_from_file() - ResultString, io::Error {let mut f File::open(hello.txt)?;let mut s String::new();f.read_to_string(mut s)?;Ok(s) }**?**其实是一个宏它的作用跟上面的match几乎一样 let mut f match f {// 打开文件成功将file句柄赋值给fOk(file) file,// 打开文件失败将错误返回(向上传播)Err(e) return Err(e), };想象一下一个设计良好的系统中肯定有自定义的错误特征错误之间很可能会存在上下级关系例如标准库中的 std::io::Error 和 std::error::Error前者是 IO 相关的错误结构体后者是一个最最通用的标准错误特征同时前者实现了后者因此 std::io::Error 可以转换为 std:error::Error。 明白了以上的错误转换? 的更胜一筹就很好理解了它可以自动进行类型提升转换 fn open_file() - ResultFile, Boxdyn std::error::Error {let mut f File::open(hello.txt)?;Ok(f) }上面代码中 File::open 报错时返回的错误是 std::io::Error 类型但是 open_file 函数返回的错误类型是 std::error::Error 的特征对象可以看到一个错误类型通过 ? 返回后变成了另一个错误类型这就是 ? 的神奇之处。 根本原因是在于标准库中定义的 From 特征该特征有一个方法 from用于把一个类型转成另外一个类型? 可以自动调用该方法然后进行隐式类型转换。因此只要函数返回的错误 ReturnError 实现了 From 特征那么 ? 就会自动把 OtherError 转换为 ReturnError。 这种转换非常好用意味着你可以用一个大而全的 ReturnError 来覆盖所有错误类型只需要为各种子错误类型实现这种转换即可。 模式匹配 忽略模式的值 _s 和 _ 有些微妙的不同_s 会将值绑定到变量而**_** 则完全不会绑定 fn main() {let s Some(String::from(Hello!));if let Some(_s) s {println!(found a string);}println!({:?}, s); } s 是一个拥有所有权的动态字符串在上面代码中我们会得到一个错误 因为 s 的值会被转移给 _s在 println! 中再次使用 s 会报错但是如果使用_去匹配则不会绑定值因为s没有被移动到_绑定 读作 at运算符允许为一个字段绑定另外一个变量。下面例子中我们希望测试 Message::Hello 的 id 字段是否位于 3…7 范围内同时也希望能将其值绑定到 id_variable 变量中以便此分支中相关的代码可以使用它。我们可以将 id_variable 命名为 id与字段同名不过出于示例的目的这里选择了不同的名称。 enum Message {Hello { id: i32 }, }let msg Message::Hello { id: 5 };match msg {Message::Hello { id: id_variable 3..7 } {println!(Found an id in range: {}, id_variable)},Message::Hello { id: 10..12 } {println!(Found an id in another range)},Message::Hello { id } {println!(Found some other id: {}, id)}, }绑定到底有什么用呢注意看第一个匹配和第二个匹配当范围匹配的时候无法直接得到这个值这个时候就可以使用绑定得到具体的值。 泛型 一个报错的泛型 fn largestT(list: [T]) - T {let mut largest list[0];for item in list.iter() {if item largest {largest item;}}largest }fn main() {let number_list vec![34, 50, 25, 100, 65];let result largest(number_list);println!(The largest number is {}, result);let char_list vec![y, m, a, q];let result largest(char_list);println!(The largest char is {}, result); }error[E0369]: binary operation cannot be applied to type T // 操作符不能用于类型T-- src/main.rs:5:17| 5 | if item largest {| ---- ^ ------- T| || T| help: consider restricting type parameter T // 考虑对T进行类型上的限制 :| 1 | fn largestT: std::cmp::PartialOrd(list: [T]) - T {| 因为 T 可以是任何类型但不是所有的类型都能进行比较因此上面的错误中编译器建议我们给 T 添加一个类型限制使用 std::cmp::PartialOrd 特征Trait对 T 进行限制特征在下一节会详细介绍现在你只要理解该特征的目的就是让类型实现可比较的功能。 简单的add例子不是所有 T 类型都能进行相加操作因此我们需要用 std::ops::AddOutput T 对 T 进行限制 fn addT: std::ops::AddOutput T(a:T, b:T) - T {a b }否则报错 error[E0369]: cannot add T to T // 无法将 T 类型跟 T 类型进行相加-- src/main.rs:2:7| 2 | a b| - ^ - T| || T| help: consider restricting type parameter T| 1 | fn addT: std::ops::AddOutput T(a:T, b:T) - T {| 感觉这里的泛型跟java c的还是不太一样更抽象一点 特征Trait 特征类似于其他语言中的接口。在之前的代码中我们也多次见过特征的使用例如 #[derive(Debug)]它在我们定义的类型(struct)上自动派生 Debug 特征接着可以使用 println!(“{:?}”, x) 打印这个类型再例如 fn addT: std::ops::AddOutput T(a:T, b:T) - T {a b }通过 std::ops::Add 特征来限制 T只有 T 实现了 **std::ops::Add **才能进行合法的加法操作毕竟不是所有的类型都能进行相加。 这些都说明一个道理特征定义了一组可以被共享的行为只要实现了特征你就能使用这组行为。 定义特征 使用trait关键字来声明一个特征 pub trait Summary {fn summarize(self) - String; }这里使用 trait 关键字来声明一个特征Summary 是特征名。在大括号中定义了该特征的所有方法在这个例子中是 fn summarize(self) - String。 特征只定义行为看起来是什么样的而不定义行为具体是怎么样的。因此我们只定义特征方法的签名而不进行实现此时方法签名结尾是 ;而不是一个 {} 为类型实现特征 pub trait Summary {fn summarize(self) - String; } pub struct Post {pub title: String, // 标题pub author: String, // 作者pub content: String, // 内容 }impl Summary for Post {fn summarize(self) - String {format!(文章{}, 作者是{}, self.title, self.author)} } impl Summary for Post {} 孤儿规则 关于特征实现与定义的位置有一条非常重要的原则如果你想要为类型 A 实现特征 T那么 A 或者 T 至少有一个是在当前作用域中定义的 例如我们可以为上面的 Post 类型实现标准库中的 Display 特征这是因为 Post 类型定义在当前的作用域中。同时我们也可以在当前包中为 String 类型实现 Summary 特征因为 Summary 定义在当前作用域中。 但是你无法在当前作用域中为 String 类型实现 Display 特征因为它们俩都定义在标准库中其定义所在的位置都不在当前作用域跟你半毛钱关系都没有看看就行了。 该规则被称为孤儿规则可以确保其它人编写的代码不会破坏你的代码也确保了你不会莫名其妙就破坏了风马牛不相及的代码。 使用特征作为函数参数 pub fn notify(item: impl Summary) {println!(Breaking news! {}, item.summarize()); }impl Summary只能说想出这个类型的人真的是起名鬼才简直太贴切了顾名思义它的意思是 实现了Summary特征 的 item 参数。 特征约束trait bound 形如T: Summary 被称为特征约束 pub fn notifyT: Summary(item: T)pub fn notify(item1: impl Summary, item2: impl Summary) {}pub fn notify(item: (impl Summary Display)) {}pub fn notifyT: Summary Display(item: T) {}fn some_functionT: Display Clone, U: Clone Debug(t: T, u: U) - i32 {}fn some_functionT, U(t: T, u: U) - i32where T: Display Clone,U: Clone Debug {}函数返回中的impl Trait fn returns_summarizable() - impl Summary {Weibo {username: String::from(sunface),content: String::from(m1 max太厉害了电脑再也不会卡,)} }因为 Weibo 实现了 Summary因此这里可以用它来作为返回值。要注意的是虽然我们知道这里是一个 Weibo 类型但是对于 returns_summarizable 的调用者而言他只知道返回了一个实现了 Summary 特征的对象但是并不知道返回了一个 Weibo 类型。 自定义类型打印输出 #![allow(dead_code)] // 允许未使用的代码use std::fmt; use std::fmt::{Display};#[derive(Debug,PartialEq)] enum FileState {Open,Closed, }#[derive(Debug)] struct File {name: String,data: Vecu8,state: FileState, }impl Display for FileState {// 第一个参数当前实例的引用// 第二个参数引用类型用于接收一个正在被修改的格式化输出流fn fmt(self, f: mut fmt::Formatter) - fmt::Result {match *self {FileState::Open write!(f, OPEN),FileState::Closed write!(f, CLOSED),}} }impl Display for File {fn fmt(self, f: mut fmt::Formatter) - fmt::Result {write!(f, {} ({}), self.name, self.state)} }impl File {fn new(name: str) - File {File {name: String::from(name),data: Vec::new(),state: FileState::Closed,}} }/*** self表示这个函数接受一个指向当前实例的引用。* * mut fmt::Formatter表示这个函数接受一个指向一个可以写入的 fmt::Formatter 的引用* fmt::Formatter 是用于格式化字符串的一种结构体。* * - fmt::Result表示这个函数返回一个 fmt::Result 值* fmt::Result 是 write!() 函数的返回类型它表示是否成功地将数据写入到指定的 fmt::Formatter 上。* * write!(f, {} ({}), self.name, self.state)* 这是调用 write!() 函数来将格式化的字符串写入到指定的 fmt::Formatter 中* 这个字符串包含了 self.name即文件名和 self.state即文件状态* 其中... 是一个字符串插槽它会在运行时被替换为我们想要显示的内容* {} 后面的内容会被替换为对应的变量或表达式的值。在这个例子中self.name 和 self.state 就是这些内容。*/fn main() {let f6 File::new(f6.txt); // 创建一个名为f6.txt的新文件对象println!({:?}, f6); // 打印调试信息输出文件对象的调试表示println!({}, f6); // 打印文件对象的字符串表示 } {:?} 使用的是 Debug 特性会输出 File 对象的详细信息包括其所有的字段及其值。 {} 使用的是 Display 特性会输出 File 对象的字符串表示形式。在上面的例子中由于我们为 File 类型实现了 Display 特性所以 {} 会输出 f6.txt (Open) 这样的字符串 关联类型 type关键字可以用来取别名但是在结合泛型使用的时候是关联类型 关联类型是 trait 定义中的类型占位符。定义的时候并不定义它的具体的类型是什么。在 impl 这个 trait 的时候才为这个关联类型赋予确定的类型。也就是说在实现的时候才知道它的具体类型是什么。 pub trait Converter {type Output;fn convert(self) - Self::Output; }struct MyInt;impl Converter for MyInt {type Output i32;fn convert(self) - Self::Output {42} }fn main() {let my_int MyInt;let output my_int.convert();println!(output is: {}, output); }输出output is: 42 参考 方法和关联函数 new 是关联函数width是方法 pub struct Rectangle {width: u32,height: u32, }impl Rectangle {pub fn new(width: u32, height: u32) - Self {Rectangle { width, height }}pub fn width(self) - u32 {return self.width;} }fn main() {let rect1 Rectangle::new(30, 50);println!({}, rect1.width()); }// let rect1 Rectangle::new(30, 50);线程学习 1.多线程的风险 由于多线程的代码是同时运行的因此我们无法保证线程间的执行顺序这会导致一些问题 竞态条件(race conditions)多个线程以非一致性的顺序同时访问数据资源死锁(deadlocks)两个线程都想使用某个资源但是又都在等待对方释放资源后才能使用结果最终都无法继续执行一些因为多线程导致的很隐晦的 BUG难以复现和解决 虽然 Rust 已经通过各种机制减少了上述情况的发生但是依然无法完全避免上述情况因此我们在编程时需要格外的小心同时本书也会列出多线程编程时常见的陷阱让你提前规避可能的风险。 2.使用spawn创建线程 use std::thread; use std::time::Duration;fn main() {thread::spawn(|| {for i in 1..10 {println!(hi number {} from the spawned thread!, i);thread::sleep(Duration::from_millis(1));}});for i in 1..5 {println!(hi number {} from the main thread!, i);thread::sleep(Duration::from_millis(1));} } 但是打印顺序是随机的主线程和子线程会随机打印。有可能主线程执行完了子线程还没有创建这取决于操作系统的调度。 等待子线程结束 use std::thread; use std::time::Duration;fn main() {let handle thread::spawn(|| {for i in 1..5 {println!(hi number {} from the spawned thread!, i);thread::sleep(Duration::from_millis(1));}});handle.join().unwrap();for i in 1..5 {println!(hi number {} from the main thread!, i);thread::sleep(Duration::from_millis(1));} } 通过调用 handle.join可以让当前线程阻塞直到它等待的子线程的结束 在上面代码中由于 main 线程会被阻塞因此它直到子线程结束后才会输出自己的 1..5move 关键字强制闭包获取其使用的环境值的所有权 use std::thread;fn main() {let v vec![1, 2, 3];let handle thread::spawn(|| {println!(Heres a vector: {:?}, v);});handle.join().unwrap(); }子线程要v的所有权但是子线程和主线程执行顺序随机。在主线程丢弃使用drop也不行。根本原因在于存在所有权冲突。 正确写法——添加move关键字 直接将所有权转移到子线程 use std::thread;fn main() {let v vec![1, 2, 3];let handle thread::spawn(move || {println!(Heres a vector: {:?}, v);});handle.join().unwrap();// 下面代码会报错borrow of moved value: v// println!({:?},v); }
http://www.dnsts.com.cn/news/216437.html

相关文章:

  • 站长工具seo推广 站长工具查询网络编程软件
  • 澧县网站设计网页设计怎么做
  • 烟台网站建设科技公司phpcms 手机网站
  • 企业网站模板源码免费王建设个人网站
  • 南京润盛建设集团有限公司网站网站如何做淘宝支付宝
  • 乡镇可以做门户网站建设网站的视频下载
  • 网站降权查询工具专业营销型网站
  • 郑州做网站建设wordpress vue react
  • 自己做网站卖东西怎么样如何做中英文切换的双语网站
  • 网站页面静态化方案网络推广的优点
  • 建设网站番禺公司做网站的费用属于什么费用
  • 公司自建网站需要多少钱宝塔wordpress更换域名
  • 网站死链接查询东莞网站优化有哪些
  • 用dw做网站结构图展厅设计图
  • 地产网站建设互动营销三九手机网手机响应式网站模版
  • 外贸精品网站建设wordpress栏目列表页
  • 阳江房产网最新楼盘精装修外贸网站建设与优化
  • 南平网站设计链接交易网
  • 政工网站建设方案影视公司排名
  • php搭建网站教程网页设计国外设计欣赏网站
  • 华秋商城官网福州seo顾问
  • 广州致峰网站建设完整的网络营销推广方案包括
  • 钓鱼网站制作网站建设招标 报告
  • 网站建设外包公司方案品牌网站源码asp
  • 智信建设职业培训学校网站房产中介网站开发费用
  • 中山网站搜索引擎优化分析网站示例
  • 响水专业做网站wordpress主题中文主题下载
  • 网站安全查询系统微信公众号运营
  • 郑州网站推广 汉狮网络制作企业网站用什么软件
  • 网站怎么搭建wordpress主题 电影