深入浅出Rust

深入浅出Rust

V1.33.0 RFC -> nightly -> beta -> stable
主版本号:API不兼容修改
次版本号:向下兼容功能新增
修订号:向下兼容问题修正

国内源:

export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static
export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup

# $HOME/.cargo/config
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'ustc'
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"

基本语法

use std::prelude::*; 将常用的std库内容导入。

Format 格式:

  • “{}” // 默认,打印Display
  • “{}” // 八进制
  • “{:x}” // 16进制,小写
  • “{:X}” // 16进制,大写
  • “{:p}” // 指针
  • “{:b}” // 二进制
  • “{:e}” // 科学计数法,小写
  • “{:E}” // 科学计数法,大写
  • “{:?}” // 打印Debug
  • “{:#?}” // 带换行和缩进的Debug
  • “{a} {b}” // 命名参数
let variable:i32 = 100;
let mut x = 5;
let (mut a, mut b) = (1,2);
// let _ = 10; // _ 合法,但是不能单独使用

let是一个模式解构,mut x是一个组合/模式。

内存管理

let x =10;
// x = 20; // error
let x = "abc"; // ok, 必须有let, x被重定义了

let s:Vec<_> = call_fuc(); 不清楚s存储的内容,但是通过call_fuc()返回值编译器自己推导。

type Age = u32 Age与u32等价。

静态变量static GL:i32 = 0; 可变全局变量读写都必须使用unsafe修饰。
在Rust中,如果用户需要使用比较复杂的全局变量初始化,推荐使用lazy_static库。

const修饰的常量可能会被优化,所以不要使用hack方式修改值,并且也无法使用let匹配。

let mut x = true;
let y = false;
x = !x; // 按位取反或者逻辑非,由数据类型决定
x = x && y; // 逻辑与
x = x & y; // 按位与
x = x || y; // 逻辑或
x = x | y; // 按位或
x = x^y; // 按位异或

let love = '❤'; // char 可以表达任何unicode字符,4字节
let c1 = '\n';          // 换行符
let c2 = '\x7f';        // 8 bit 字符变量
let c3 = '\u{7FFF}';    // unicode字符

let a1:u8 = b'A'; // 用来表示ascii字符

在Rust中,我们可以为任何一个类型添加方法,整型也不例外。

let x : i32 = 9;
println!("9 power 3 = {}", x.pow(3));

针对溢出问题:(u8整数溢出等)
默认情况下,在debug模式下编译器会自动插入整数溢出检查,一旦发生溢出,则会引发panic;
在release模式下,不检查整数溢出,而是采用自动舍弃高位的方式。

指针:

  • Box 指向类型T的,有所有权的指针,有权释放内存
  • &T 指向类型T的借用指针,即引用,无权释放内存
  • &mut T 指向类型T的mut借用指针,可以写数据,无权释放内存
  • *const T 指向类型T的只读裸指针,无生命周期信息,不可写数据
  • *mut T 指向类型T的可读写裸指针,无生命周期信息,可写数据
  • Rc 智能指针,指向类型T的引用计数指针,共享所有权,线程不安全
  • Arc 智能指针,指向类型T的原子型引用计数指针,共享所有权,线程安全
  • Cow<'a,T> 智能指针,指向类型T的写时复制指针(clone-on-write),可能借用,也可能所有权。

类型转换:

  • as 普通类型转换可用as, 跨度大的类型可能需要多个as转换
  • From into, as_xxx 专用转换接口trait. 适合无法直接转换的类型

通过std::mem::size_of可测量空间占用。unit和空struct占用空间为0。

tuple, a.0,a.1 也可以访问。什么都没有的tuple叫做unit。
struct, 内存布局与C不同,所以如果要在FFI中使用需要添加#[repr(C)]类似的结构
tuple struct
enum, 多个类型或的关系。

递归类型:
rust struct Recursive { data: i32, rec: Recursive, } // 上面错误,因为无法确定rec的内存大小 // 解决办法很简单,用指针间接引用就可以了,因为指针的大小是固定的 struct Recursive { data: i32, rec: Box, }

LEFT OP=RIGHT这种写法,含义等同于LEFT=LEFT OP RIGHT。所以,y+=x的意义相当于y=y+x,依此类推。
Rust不支持++、–运算符,请使用+=1、-=1替代。

语句和表达式的区分方式是后面带不带分号(;)。
如果带了分号,意味着这是一条语句,它的类型是();如果不带分号,它的类型就是表达式的类型。

loop可以选择跳出位置 或 返回一个值:

fn main1() {
    // A counter variable
    let mut m = 1;
    let n = 1;

    'a: loop {
        if m < 100 {
            m += 1;
        } else {
            'b: loop {
                if m + n > 50 {
                    println!("break");
                    break 'a;
                } else {
                    continue 'a;
                }
            }
        }
    }
}

fn main2() {
    let v = loop {
        break 10;
    };
    println!("{}", v);
}

Rust支持一种特殊的发散函数(Diverging functions),它的返回类型是感叹号!

当前版本的Rust暂时还不支持尾递归优化,因此如果递归调用层次太多的话,是有可能撑爆栈空间的。尽量使用其他方式进行。

抽象表达

trait 是Rust中一个非常重要的概念。中文翻译为“特征”、“特点”、“特性”。

trait Shape {
    fn area(&self) -> f64;
}

其中的self很关键,所有的trait都有一个隐藏的Self(大写),代表当前这个实现了trait的具体类型,trait定义的函数也被称为“关联函数”。
函数的第一个参数如果是self(小写,一定要在第一个参数位置上,称为receiver 接收者)则说明他是一个方法,可以通过点访问。没有receiver的函数称之为静态函数,只能通过双冒号访问。
trait方法的self参数可以是基于它的一个变体self, &self, mut self, &mut self

并发模型

use std::sync::Arc;
use std::sync::Mutex; // RwLock
use std::thread;

const COUNT: u32 = 1000_0000;

fn main() {
    let global = Arc::new(Mutex::new(0));
    let clone1 = global.clone();
    let th1 = thread::spawn(move || {
        for _ in 0..COUNT {
            let mut v = clone1.lock().unwrap();
            *v += 1;
        }
    });
    let clone2 = global.clone();
    let th2 = thread::spawn(move || {
        for _ in 0..COUNT {
            let mut v = clone2.lock().unwrap();
            *v += 1;
        }
    });
    th1.join().ok();
    th2.join().ok();
    println!("the final value: {:?}", global);
}

跨线程通信

use std::sync::mpsc::channel;
use std::thread;

// sync_channel是阻塞通道,用法同channel.

fn main() {
    let (tx, rx) = channel();
    for j in 0..5{
        let tx_cp = tx.clone();
        thread::spawn( move||{
            for i in 0..10{
                tx_cp.send(i+j*100).unwrap();
            }
        });
    }
    drop(tx); // 减一个引用计数

    while let Ok(r) = rx.recv() {
        println!("{}", r);
    }
    println!("Over");
}

FFI

--crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
其中cdylib和staticlib就是与C的ABI兼容的动态库和静态库,一般使用有两种方式:

  • 在编译命令中添加rustc --crate-type=staticlib xxx.ts
  • 在源代码入口处指定#![crate-type="staticlib"]

在rust中extern fn等价于extern "C" fn,使用#[no_mangle]修饰函数,避免名字重整。

Demo
RUST生成C库(动态,静态)

// Cargo.toml

// [lib]
// crate-type = ["dylib"]  // staticlib

// [dependencies]
// libc=""

// lib.rs
extern crate libc;
use libc::c_char;
#[no_mangle]
pub extern fn rust_capitalize(s: *mut c_char) {
    unsafe {
        let mut p = s as *mut u8;
        while *p != 0 {
            let ch = char::from(*p);
            if ch.is_ascii() {
                let upper = ch.to_ascii_uppercase();
                *p = upper as u8;
            }
            p = p.offset(1);
        }
    }
}

cargo build --release --lib

RUST调用C库(静态调用)

extern crate libc;
use libc::*;
#[link(name="simple_math")]
extern "C" {
    fn add_square(a:c_int, b:c_int) -> c_int;
}

fn main(){
    let r = unsafe{ add_square(2,3) };
    println!("{}", r);
}

你可能感兴趣的:(Rust)