rust program英文和汉语混合笔记(4)

闭包可以捕获其环境并访问其被定义的作用域的变量。当闭包从环境中捕获一个值,闭包会在闭包体中储存这个值(就像函数的传入参数栈)以供使用。这会使用内存并产生额外的开销,在更一般的场景中,当我们不需要闭包来捕获环境时,我们不希望产生这些开销。因为函数从未允许捕获环境,定义和使用函数也就从不会有这些额外开销。可以move。

迭代器是 惰性的(lazy)

#![allow(unused)]
fn main() {
pub trait Iterator {
type Item;

fn next(&mut self) -> Option<Self::Item>;

// 此处省略了方法的默认实现

}
}
#[cfg(test)]
mod tests {
#[test]
fn iterator_demonstration() {
let v1 = vec![1, 2, 3];

    let mut v1_iter = v1.iter();

    assert_eq!(v1_iter.next(), Some(&1));
    assert_eq!(v1_iter.next(), Some(&2));
    assert_eq!(v1_iter.next(), Some(&3));
    assert_eq!(v1_iter.next(), None);
}

}
Iter,into_iter,iter_mut
编写自己的迭代器:
struct Counter {
count: u32,
}

impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
}
impl Iterator for Counter {
type Item = u32;//占位

fn next(&mut self) -> Option<Self::Item> {
    if self.count < 5 {
        self.count += 1;
        Some(self.count)
    } else {
        None
    }
}

}

Cargo
[profile.dev]
opt-level = 0

[profile.release]
opt-level = 3
文档注释使用三斜杠 /// 而不是两斜杆以支持 Markdown 注解来格式化文本。文档注释就位于需要文档的项的之前
为了方便起见,运行 cargo doc --open 会构建当前 crate 文档(同时还有所有 crate 依赖的文档)的 HTML 并在浏览器中打开。
还有另一种风格的文档注释,//!,这为包含注释的项,而不是位于注释之后的项增加文档,就是对文档注释的文档注释。这通常用于 crate 根文件(通常是 src/lib.rs)或模块的根文件为 crate 或模块整体提供文档。
//! # Art
//!
//! A library for modeling artistic concepts.

pub use self::kinds::PrimaryColor;
pub use self::kinds::SecondaryColor;
pub use self::utils::mix;

pub mod kinds {
// --snip–
/// The primary colors according to the RYB color model.
pub enum PrimaryColor {
Red,
Yellow,
Blue,
}

/// The secondary colors according to the RYB color model.
pub enum SecondaryColor {
    Orange,
    Green,
    Purple,
}

}

pub mod utils {
// --snip–
use crate::kinds:😗;

/// Combines two primary colors in equal amounts to create
/// a secondary color.
pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
    SecondaryColor::Orange
}

}//pub use将最深层函数导出到顶层,以方便调用者use crates名称::函数名两层结构就行

现在我们创建了一个账号,保存了 API token,为 crate 选择了一个名字,并指定了所需的元数据,你已经准备好发布了!发布 crate 会上传特定版本的 crate 到 crates.io 以供他人使用。
cargo.toml:
[package]
name = “guessing_game”
version = “0.1.0”
edition = “2021”
description = “A fun game where you guess what number the computer has chosen.”
license = “MIT OR Apache-2.0”
[dependencies]

cargo publish
撤回某个版本会阻止新项目开始依赖此版本,不过所有现存此依赖的项目仍然能够下载和依赖这个版本。从本质上说,撤回意味着所有带有 Cargo.lock 的项目的依赖不会被破坏,同时任何新生成的 Cargo.lock 将不能使用被撤回的版本。
为了撤回一个 crate,运行 cargo yank 并指定希望撤回的版本:
$ cargo yank --vers 1.0.1
也可以撤销撤回操作,并允许项目可以再次开始依赖某个版本,通过在命令上增加 --undo:
$ cargo yank --vers 1.0.1 --undo

文件名: adder/Cargo.toml:
[dependencies]
add_one = { path = “…/add_one” }才能在main.rs中使用use add_one;
文件名: add-one/Cargo.toml:
[dependencies]
rand = “0.8.3”//才能在add_one.rs中使用use rand;
Cargo 只在根目录保存了一个cargo.lock确保了工作空间中任何使用 rand 的 crate 都采用相同的版本。在整个工作空间中使用相同版本的 rand 节省了空间,因为这样就无需多个拷贝并确保了工作空间中的 crate 将是相互兼容的。
cargo test -p 测试名1 仅测试一个
cargo test,测试全部

如果你选择向 crates.io发布工作空间中的 crate,每一个工作空间中的 crate 需要单独发布。cargo publish 命令并没有 --all 或者 -p 参数,所以必须进入每一个 crate 的目录并运行 cargo publish 来发布工作空间中的每一个 crate。
随着项目增长,考虑使用工作空间:每一个更小的组件比一大块代码要容易理解。如果它们经常需要同时被修改的话,将 crate 保持在工作空间中更易于协调他们的改变。

Cargo install二进制文件
通常 crate 的 README 文件中有该 crate 是库、二进制目标还是两者都是的信息。
$ cargo install ripgrep
Updating crates.io index
Downloaded ripgrep v11.0.2
Downloaded 1 crate (243.3 KB) in 0.88s
Installing ripgrep v11.0.2
–snip–
Compiling ripgrep v11.0.2
Finished release [optimized + debuginfo] target(s) in 3m 10s
Installing ~/.cargo/bin/rg
Installed package ripgrep v11.0.2 (executable rg)

Cargo 的设计使得开发者可以通过新的子命令来对 Cargo 进行扩展,而无需修改 Cargo 本身。如果 $PATH 中有类似 cargo-something 的二进制文件,就可以通过 cargo something 来像 Cargo 子命令一样运行它。像这样的自定义命令也可以运行 cargo --list 来展示出来。能够通过 cargo install 向 Cargo 安装扩展并可以如内建 Cargo 工具那样运行他们是 Cargo 设计上的一个非常方便的优点!

智能指针通常使用结构体实现。智能指针区别于常规结构体的显著特性在于其实现了 Deref 和 Drop trait。Deref trait 允许智能指针结构体实例表现的像引用一样,这样就可以编写既用于引用、又用于智能指针的代码。Drop trait 允许我们自定义当智能指针离开作用域时运行的代码。本章会讨论这些 trait 以及为什么对于智能指针来说他们很重要。
每次当我们在代码中使用 * 时, * 运算符都被替换成了先调用 deref 方法再接着使用 * 解引用的操作,且只会发生一次,不会对 * 操作符无限递归替换
Deref 强制转换(deref coercions)是 Rust 在函数或方法传参上的一种便利。Deref 强制转换只能作用于实现了 Deref trait 的类型。当所涉及到的类型定义了 Deref trait,Rust 会分析这些类型并使用任意多次 Deref::deref 调用以获得匹配参数的类型。这些解析都发生在编译时(编译慢),所以利用 Deref 强制转换并没有运行时损耗!
Rust 在发现类型和 trait 实现满足三种情况时会进行 Deref 强制转换:
当 T: Deref<Target=U> 时从 &T 到 &U。
当 T: DerefMut<Target=U> 时从 &mut T 到 &mut U。
当 T: Deref<Target=U> 时从 &mut T 到 &U。
std::mem::drop 位于 prelude,提前Drop内存:
文件名: src/main.rs
fn main() {
let c = CustomSmartPointer {
data: String::from(“some data”),
};
println!(“CustomSmartPointer created.”);
drop©;
println!(“CustomSmartPointer dropped before the end of main.”);
}

Rc(t)
也可以调用 a.clone() 而不是 Rc::clone(&a),不过在这里 Rust 的习惯是使用 Rc::clone。Rc::clone 的实现并不像大部分类型的 clone 实现那样对所有数据进行深拷贝。Rc::clone 只会增加引用计数,这并不会花费多少时间。深拷贝可能会花费很长时间。通过使用 Rc::clone 进行引用计数,可以明显的区别深拷贝类的克隆和增加引用计数类的克隆。当查找代码中的性能问题时,只需考虑深拷贝类的克隆而无需考虑 Rc::clone 调用。
特定情况下,令一个值在其方法内部能够修改自身,而在其他代码中仍视为不可变,是很有用的。值方法外部的代码就不能修改其值了。RefCell/Cell 是一个获得内部可变性的方法。RefCell 并没有完全绕开借用规则,编译器中的借用检查器允许内部可变性并相应地在运行时检查借用规则。如果违反了这些规则,会出现 panic 而不是编译错误。
如下为选择 Box,Rc 或 RefCell 的理由:
Rwlock:多读一写。先区分读还是写,如读,再确认几个读。
Rc 允许相同数据有多个所有者,多个儿子有一个父亲,sons are all dropped,could father be mutable or droped;Box 和 RefCell(只能有一个mut权限) 有单一所有者。
Weak是多个借用人,多个父亲借用一个儿子(只保存一个深拷贝,否则,儿子没了,找不到数据,儿子经常没了)
Box 允许在编译时执行不可变或可变借用检查;Rc仅允许在编译时执行不可变借用检查(是否只有一个mut权限人);RefCell 允许在运行时执行不可变或可变借用检查。
因为 RefCell 允许在运行时执行可变借用检查,所以我们可以在即便 RefCell 自身是不可变(例如被rc包裹)的情况下修改其内部的值。
#[derive(Debug)]
enum List {
Cons(Rc<RefCell>, Rc),
Nil,
}

use crate::List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;

fn main() {
let value = Rc::new(RefCell::new(5));

let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));

let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a));
let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a));

*value.borrow_mut() += 10;//*来解引用 Rc<T> 以获取其内部的 RefCell<T> 值。borrow_mut 方法返回 RefMut<T> 智能指针,可以对其使用解引用运算符并修改其内部值。

println!("a after = {:?}", a);
println!("b after = {:?}", b);
println!("c after = {:?}", c);

}
双向引用用RefCell(Rc())加RefCell(Weak())避免
use std::cell::RefCell;
use std::rc::{Rc, Weak};

#[derive(Debug)]
struct Node {
value: i32,
parent: RefCell<Weak>,
children: RefCell<Vec<Rc>>,
}

fn main() {
let leaf = Rc::new(Node {
value: 3,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});

println!(
    "leaf strong = {}, weak = {}",
    Rc::strong_count(&leaf),
    Rc::weak_count(&leaf),
);

{
    let branch = Rc::new(Node {
        value: 5,
        parent: RefCell::new(Weak::new()),
        children: RefCell::new(vec![Rc::clone(&leaf)]),
    });

    *leaf.parent.borrow_mut() = Rc::downgrade(&branch);

    println!(
        "branch strong = {}, weak = {}",
        Rc::strong_count(&branch),
        Rc::weak_count(&branch),
    );

    println!(
        "leaf strong = {}, weak = {}",
        Rc::strong_count(&leaf),
        Rc::weak_count(&leaf),
    );
}

println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
println!(
    "leaf strong = {}, weak = {}",
    Rc::strong_count(&leaf),
    Rc::weak_count(&leaf),
);

}

并发/并行concurrence/synchronize
use std::thread;
use std::time::Duration;

fn main() {
let handle = thread::spawn(|| {
for i in 1…10 {
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().unwrap();如果放在这儿,随机执行主子线程,主线程会等待子线程执行完毕再退出。

}

一个日益流行的确保安全并发的方式是 消息传递(message passing),这里线程或 actor 通过发送包含数据的消息来相互沟通。这个思想来源于 Go 编程语言文档中 的口号:“不要通过共享内存来通讯;而是通过通讯来共享内存。”(“Do not communicate by sharing memory; instead, share memory by communicating.”)

我们使用了 recv,它是 receive 的缩写。这个方法会阻塞主线程执行直到从通道中接收一个值。一旦发送了一个值,recv 会在一个 Result<T, E> 中返回它。当通道发送端关闭,recv 会返回一个错误表明不会再有新的值到来了。

try_recv 不会阻塞,相反它立刻返回一个 Result<T, E>:Ok 值包含可用的信息,而 Err 值代表此时没有任何消息。如果线程在等待消息过程中还有其他工作时使用 try_recv 很有用:可以编写一个循环来频繁调用 try_recv,在有可用消息时进行处理,其余时候则处理一会其他工作直到再次检查。
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
// --snip–

let (tx, rx) = mpsc::channel();

let tx1 = tx.clone();
thread::spawn(move || {
    let vals = vec![
        String::from("hi"),
        String::from("from"),
        String::from("the"),
        String::from("thread"),
    ];

    for val in vals {
        tx1.send(val).unwrap();
        thread::sleep(Duration::from_secs(1));
    }
});

thread::spawn(move || {
    let vals = vec![
        String::from("more"),
        String::from("messages"),
        String::from("for"),
        String::from("you"),
    ];

    for val in vals {
        tx.send(val).unwrap();//tx is consumed
        thread::sleep(Duration::from_secs(1));
    }
});

for received in rx {
    println!("Got: {}", received);
}

// --snip--

}

这发生于当一个操作需要锁住两个资源而两个线程各持一个锁,这会造成它们永远相互等待。如果你对这个主题感兴趣,尝试编写一个带有死锁的 Rust 程序,接着研究任何其他语言中使用互斥器的死锁规避策略并尝试在 Rust 中实现他们。标准库中 Mutex 和 MutexGuard 的 API 文档会提供有用的信息。

然而有两个并发概念是内嵌于语言中的:std::marker 中的 Sync 和 Send trait。
通过 Send 允许在线程间转移所有权
Send 标记 trait 表明实现了 Send 的类型值的所有权可以在线程间传送。几乎所有的 Rust 类型都是Send 的,不过有一些例外,包括 Rc:这是不能 Send 的,因为如果克隆了 Rc 的值并尝试将克隆的所有权转移到另一个线程,这两个线程都可能同时更新引用计数。为此,Rc 被实现为用于单线程场景,这时不需要为拥有线程安全的引用计数而付出性能代价。因此,Rust 类型系统和 trait bound 确保永远也不会意外的将不安全的 Rc 在线程间发送。
几乎所有基本类型都是 Send (转移所有权)的,除了第十九章将会讨论的裸指针(raw pointer)。
对于任意类型 T,如果 &T(T 的不可变引用)是 Send 的话 T 就是 Sync 的,这意味着其引用就可以安全的发送到另一个线程。类似于 Send 的情况,基本类型是 Sync 的,完全由 Sync 的类型组成的类型也是 Sync 的。

智能指针 Rc 也不是 Sync 的,出于其不是 Send 相同的原因。RefCell(第十五章讨论过)和 Cell 系列类型不是 Sync 的。RefCell 在运行时所进行的借用检查也不是线程安全的。Mutex 是 Sync 的,正如 “在线程间共享 Mutex” 部分所讲的它可以被用来在多线程中共享访问。因为并发相关的不可变性与Rc,。RefCell/Cell可以并发改变值是矛盾的,他们不是线程安全的,不能保证被传递时打断。Sync必须是线程安全的,原子的,所以,Arc就是Sync的。所以,单线程用Rc,多线程用Arc.<Mutex>
Rust由于有所有权可以保证用锁共享内存而不必用通道传递消息

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
let counter = Arc::new(Mutex::new(0));//Atomic RC(reference counter)保证thread不会被打断
let mut handles = vec![];

for _ in 0..10 {
    let counter = Arc::clone(&counter);
    let handle = thread::spawn(move || {
        let mut num = counter.lock().unwrap();

        *num += 1;
    });
    handles.push(handle);
}

for handle in handles {
    handle.join().unwrap();
}

println!("Result: {}", *counter.lock().unwrap());

}

正如之前提到的,因为 Rust 本身很少有处理并发的部分内容,有很多的并发方案都由 crate 实现。他们比标准库要发展的更快;请在网上搜索当前最新的用于多线程场景的 crate。

Rust 提供了用于消息传递的通道,和像 Mutex 和 Arc 这样可以安全的用于并发上下文的智能指针。类型系统和借用检查器会确保这些场景中的代码,不会出现数据竞争和无效的引用。一旦代码可以编译了,我们就可以坚信这些代码可以正确的运行于多线程环境,而不会出现其他语言中经常出现的那些难以追踪的 bug。并发编程不再是什么可怕的概念:无所畏惧地并发吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值