Rust is a systems programming language that runs blazingly fast,prevents segfaults,and guarantees thread safety.
内存不安全的行为: - 空指针 - 野指针 - 悬空指针 - 使用未初始化的指针 - 非法释放 - 缓冲区溢出 - 执行非法函数指针 - 数据竞争
不认为是内存安全的行为: - 内存泄漏
一个变量只能存在一个可写借用,可写借用可以修改变量的内容,但是不能转移所有权
编译时大小已知,并且存储在栈上(值类型)一般都有copy trait,称之为浅拷贝.
Copy是浅拷贝,是编译器自动调用,Clone是深拷贝,程序员手工调用.
& 引用实际上可以认为是所有权的临时借用 (不全是c语言中的指针,只不过用起来像而已) * 解引用
引用有两种 不可变引用与可变引用.
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
###元组(tuple) 元组是没有字段名的结构体
struct Color(i32, i32, i32);
枚举是一个很多语言都有的功能,不过不同语言中其功能各不相同。Rust 的枚举与 F#、OCaml 和 Haskell 这样的函数式编程语言中的 代数数据类型(algebraic data types)最为相似。
与C/go语言中的枚举完全不同,没有对应的整数值.
一个典型的例子:
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
enum Option<T> {
Some(T),
None,
}
match必须是穷尽的
if let是一种专门的用法
如何将不同模块分割到单独的文件中
通过pub use重导出
通过cargo.toml中[dependencies]来使用外部包
use std::cell::Cell;
fn main() {
let data : Cell<i32> = Cell::new(100); let p = &data; vdata.set(10); println!("{}", p.get());
p.set(20); println!("{:?}", data);
}
所谓的内部可变性,就是不用mut,一样可以修改Cell包括的内容. cell导出接口
impl<T> Cell<T> {
//这个要求是可写借用,其他都是只读借用,但是还是可以修改其内容
pub fn get_mut(&mut self) -> &mut T {}
pub fn set(&self, val: T) { }
pub fn swap(&self, other: &Self) { }
pub fn replace(&self, val: T) -> T { }
pub fn into_inner(self) -> T { }
//Copy实际上就是get
impl<T:Copy> Cell<T> {
pub fn get(&self) -> T { }
}
RefCell接口的定义
impl<T: ?Sized> RefCell<T> {
pub fn borrow(&self) -> Ref<T> { }
pub fn try_borrow(&self) -> Result<Ref<T>,BorrowError> {}
pub fn borrow_mut(&self) -> RefMut<T> { }
pub fn try_borrow_mut(&self) -> Result<RefMut<T>, BorrowMutError> { }
pub fn get_mut(&mut self) -> &mut T { }
他和cell不一样的是,他并不能直接修改或者读取其包含的内容,而是通过borrow以及borrow_mut来获取其对应的Ref
如何选择Cell和RefCell? 如果你只需要整体性地存⼊、取出T,那么就选 Cell。如果你需要有个可读写指针指向这个T修改它,那么就选RefCell。
Rust会自动解引用,这看起来就像类型自动转换一样.
比如Vec
use std::rc::Rc;
fn main() { let s = Rc::new(String::from("hello")); println!("{:?}", s.bytes()); }
这个例子中s类型为Rcs.deref().deref().bytes()
编译器会自动无限deref尝试,直到不能deref下去. 如果不能自动判断,需要手工介入
&*和& * 是不一样的
let s=Box::new(String::new());
&*s会被直接当成s.deref(),而不是*s先把内部数据移走然后去借用
### 宏
#### 宏展开方法
cargo rustc -- -Z unstable-options --pretty=expanded
### 临时变量的生命周期
来自于这篇讨论借用一个临时变量是有效的
Temporary lifetimes
When using a value expression in most place expression contexts, a temporary unnamed memory location is created initialized to that value and the expression evaluates to that location instead
This applies, because String::new() is a value expression and being just below &mut it is in a place expression context. Now the reference operator only has to pass through this temporary memory location, so it becomes the value of the whole right side (including the &mut).
When a temporary value expression is being created that is assigned into a let declaration, however, the temporary is created with the lifetime of the enclosing block instead
Since it is assigned to the variable it gets a lifetime until the end of the enclosing block.
This also answers this question about the difference between
let a = &String::from("abcdefg"); // ok!
and
let a = String::from("abcdefg").as_str(); // compile error 这样之所以不行是因为as_str()借用的生命周期和a相同,但是被借用对象string::from()返回的临时变量的生命周期依然是这一行
In the second variant the temporary is passed into as_str(), so its lifetime ends at the end of the statement.
通俗来说,就是临时变量的生命周期就是这一行语句,但是如果返回的变量使用let绑定的话,那么其生命周期就变成了最近的那个{}
若是有where T:’static 的约束,意思则是,类型T⾥⾯不包含任何指向短⽣命周期的借⽤指针, 意思是要么完全不包含任何借⽤,要么可以有指向’static的借⽤指针。
到⽬前为 ⽌,for<‘a>Fn(&‘a Arg)->&‘a Ret这样的语法,只能⽤于⽣命周期参数, 不能⽤于任意泛型类型。
fn calc_by<'a, F>(var: &'a i32, f: F) -> i32 where F: for<'f> Fn(&'f i32) -> i32 {
let local = *var;
f(&local)
}
Captures are written as a dollar ($) followed by an identifier, a colon (:), and finally the kind of capture, which must be one of the following:
tt: a single token tree
item: anything.
block: anything.
stmt: => , ;
pat: => , = if in
expr: => , ;
ty: , => : = > ; as
ident: anything.
path: , => : = > ; as
meta: anything.
tt: anything.
trace_macros!(true); 设置以后能够将宏展开, 打印每一个步骤. trace_macros!(false); 停止打印
此宏能够在终端打印传递给他的每一个token,方便调试
rustc -Z unstable-options –pretty expanded hello.rs
Rust提供了Send和Sync两个标签trait,它们是Rust⽆数据竞争并发的基⽯。
如果Executor是多线程的,那么每一个.await都可能引发任务在不同线程之间发送 因此任务必须实现Send+Sync
Similarly, it isn’t a good idea to hold a traditional non-futures-aware lock across an .await, as it can cause the threadpool to lock up: one task could take out a lock, .await and yield to the executor, allowing another task to attempt to take the lock and cause a deadlock. To avoid this, use the Mutex in futures::lock rather than the one from std::sync.
在Excutor中也是不适宜使用普通的lock,要使用futures提供的锁
一个简单的例子,不考虑复杂的
let fut_one = ...;
let fut_two = ...;
async move {
fut_one.await;
fut_two.await;
}
这段代码最终会被翻译成如下:
// The `Future` type generated by our `async { ... }` block
struct AsyncFuture {
fut_one: FutOne,
fut_two: FutTwo,
state: State,
}
// List of states our `async` block can be in
enum State {
AwaitingFutOne,
AwaitingFutTwo,
Done,
}
impl Future for AsyncFuture {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
loop {
match self.state {
State::AwaitingFutOne => match self.fut_one.poll(..) {
Poll::Ready(()) => self.state = State::AwaitingFutTwo,
Poll::Pending => return Poll::Pending,
}
State::AwaitingFutTwo => match self.fut_two.poll(..) {
Poll::Ready(()) => self.state = State::Done,
Poll::Pending => return Poll::Pending,
}
State::Done => return Poll::Ready(()),
}
}
}
}
Pin主要是给借用服务的&T和&mut T,确保被借用的对象不被移动.
借用的使用例子
async {
let mut x = [0; 128];
let read_into_buf_fut = read_into_buf(&mut x);
read_into_buf_fut.await;
println!("{:?}", x);
}
这部分代码如何翻译呢?
struct ReadIntoBuf<'a> {
buf: &'a mut [u8], // points to `x` below
}
struct AsyncFuture {
x: [u8; 128],
read_into_buf_fut: ReadIntoBuf<'what_lifetime?>,
}
关键问题是&T本质上是一个指针,那么AsyncFuture随时可能被移动,那么ReadIntoBuf中的这个&T指针肯定会失效
因此需要使用Pin来保存.
Pin有几种Pin<&mut T>, Pin<&T>, Pin
理解Stream的一个关键就是,返回Ready,里面可能是Some,也可能是None,如果是None,表示Stream关闭了.
trait Stream {
/// The type of the value yielded by the stream.
type Item;
/// Attempt to resolve the next item in the stream.
/// Retuns `Poll::Pending` if not ready, `Poll::Ready(Some(x))` if a value
/// is ready, and `Poll::Ready(None)` if the stream has completed.
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>)
-> Poll<Option<Self::Item>>;
}
async fn send_recv() {
const BUFFER_SIZE: usize = 10;
let (mut tx, mut rx) = mpsc::channel::<i32>(BUFFER_SIZE);
tx.send(1).await.unwrap();
tx.send(2).await.unwrap();
drop(tx);
// `StreamExt::next` is similar to `Iterator::next`, but returns a
// type that implements `Future<Output = Option<T>>`.
assert_eq!(Some(1), rx.next().await);
assert_eq!(Some(2), rx.next().await);
assert_eq!(None, rx.next().await);
}
atomic Ordering总共有五种顺序 1. 排序一致性顺序: SeqCst。 2. 自由顺序: Relaxed (感觉类似c语言中的volatile) 3. 其他: Release,Acquire,AcqRel Rust支持的5种内存顺序与其底层的LLVM支持的内存顺序是一致的
在这种模型下,store()使用memory_order_release,而load()使用memory_order_acquire。这种模型有两种效果,第一种是可以限制 CPU 指令的重排:
在store()之前的所有读写操作,不允许被移动到这个store()的后面。 在load()之后的所有读写操作,不允许被移动到这个load()的前面。 参考理解 C++ 的 Memory Order
std::atomic<bool> ready{ false };
int data = 0;
void producer()
{
data = 100; // A
ready.store(true, std::memory_order_release); // B
}
void consumer()
{
while (!ready.load(std::memory_order_acquire)) // C
;
assert(data == 100); // never failed // D
}
Relaxed operation: there are no synchronization or ordering constraints imposed on other reads or writes, only this operation’s atomicity is guaranteed (see Relaxed ordering below)
A load operation with this memory order performs a consume operation on the affected memory location: no reads or writes in the current thread dependent on the value currently loaded can be reordered before this load. Writes to data-dependent variables in other threads that release the same atomic variable are visible in the current thread. On most platforms, this affects compiler optimizations only (see Release-Consume ordering below)
A load operation with this memory order performs the acquire operation on the affected memory location: no reads or writes in the current thread can be reordered before this load. All writes in other threads that release the same atomic variable are visible in the current thread (see Release-Acquire ordering below)
A store operation with this memory order performs the release operation: no reads or writes in the current thread can be reordered after this store. All writes in the current thread are visible in other threads that acquire the same atomic variable (see Release-Acquire ordering below) and writes that carry a dependency into the atomic variable become visible in other threads that consume the same atomic (see Release-Consume ordering below).
A read-modify-write operation with this memory order is both an acquire operation and a release operation. No memory reads or writes in the current thread can be reordered before or after this store. All writes in other threads that release the same atomic variable are visible before the modification and the modification is visible in other threads that acquire the same atomic variable.
A load operation with this memory order performs an acquire operation, a store performs a release operation, and read-modify-write performs both an acquire operation and a release operation, plus a single total order exists in which all threads observe all modifications in the same order (see Sequentially-consistent ordering below)
?Sized对于T的约束主要是指可以是固定大小类型也可以说DST,对于DST来说,可以定义 但是用的时候只能是指针,比如下面的例子.
#[derive(Debug)]
struct FooSized<'a,T: ?Sized>(&'a T);
use std::cell::UnsafeCell;
fn main() {
let h_s = FooSized("hello");
println!("{:?}", h_s);
let _s=UnsafeCell::new(h_s.0);
}
pub trait AsRef<T: ?Sized> {
fn as_ref(&self) -> &T;
}
pub trait Borrow<Borrowed: ?Sized> {
fn borrow(&self) -> &Borrowed;
}
从形式上看,他们是完全一样的,只是出于设计的目的不一样,不同的名字是让使用者在不同的场景下使用.
Choose Borrow when you want to abstract over different kinds of borrowing, or when you’re building a data structure that treats owned and borrowed values in equivalent ways, such as hashing and comparison.
Choose AsRef when you want to convert something to a reference directly, and you’re writing generic code.
在看tokio的代码中发现的,
pub(crate) struct CachedParkThread {
_anchor: PhantomData<Rc<()>>,
}
/// Used to ensure the invariants are respected
struct GenerationGuard<'a> {
/// Worker reference
worker: &'a Worker,
/// Prevent `Sync` access
_p: PhantomData<Cell<()>>,
}
pub(crate) struct Enter {
_p: PhantomData<RefCell<()>>,
}
这里的用法目的是阻止Send,Sync的自动实现,防止这些结构体跨线程传递.