Rust常用操作

Rust常用操作

列举常用的一些功能的demo或者实现形式。

当前路径&遍历目录

use std::fs;
use std::env;

fn main() {

    println!("curdir: {}, curexe: {}", env::current_dir().unwrap().display(), env::current_exe().unwrap().display());

    println!("cd to ../..\n");
    env::set_current_dir("../..").unwrap();
    println!("ls: {}", env::current_dir().unwrap().display());

    let paths = fs::read_dir("./").unwrap();
    for path in paths {
        let f = path.unwrap().path();
        println!("{} {}", if f.is_file() { "f" } else { "d" }, f.display());
    }

}

系统信息

这个没有官方的包,需要使用第三方 sys-info.
获取信息十分有限。

extern crate sys_info;

fn main() {
    println!("操作系统:{}", sys_info::os_type().unwrap());
    println!("版本:{}", sys_info::os_release().unwrap()); // win10 is 6.2
    println!("名称:{}", sys_info::hostname().unwrap());

    println!("CPU个数:{}", sys_info::cpu_num().unwrap());
    println!("CPU速度:{:.2}GHz", sys_info::cpu_speed().unwrap() as f64 / 1024.0);
    println!("平均负载:{:.2}%", sys_info::loadavg().unwrap().one * 100.0);

    let minfo = sys_info::mem_info().unwrap();
    println!("内存信息:{:.2}G/{:.2}G free", minfo.total as f64/1024.0/1024.0, minfo.free as f64/1024.0/1024.0);

    let dinfo = sys_info::disk_info().unwrap();
    println!("当前磁盘分区信息:{:.2}G/{:.2}G free", dinfo.total as f64/1024.0/1024.0, dinfo.free as f64/1024.0/1024.0);
}

stdio

use std::io;
use std::io::prelude::*;

fn main() {
    let mut input = String::new();
    println!("test io input.");
    io::stdout().write("put your code: ".as_bytes()).unwrap();
    io::stdout().flush().unwrap();

    io::stdin().read_line(&mut input).unwrap();
    println!("your input: {}", input);

}
// try! 和 ? 作用雷同,都需要在父函数中返回Result才能使用。

数据遍历&汇聚

use std::io;
use std::io::prelude::*;

fn main() {
    let a = (1..5).collect::>();
    for i in &a {
        println!("1 {}", i);
    }
    a.into_iter().for_each(|v| {
        println!("2 {}", v)
    });

    let m = (1..20).fold(0u64, |x, y| y + x);
    println!("{}", m);
    let m = (1..20).map(|x| 10 * x);
    println!("{}", m);
    let m = (1..20).filter(|x| x%3 == 0);
    println!("{}", m);
}

参数解析pro

extern crate clap;
use clap::{Arg, App, SubCommand};

fn main() {
    let matches = App::new("My Super Program")
        .version("1.0")
        .author("Kevin K. ")
        .about("Does awesome things")
        .arg(Arg::with_name("config")
            .short("c")
            .long("config")
            .value_name("FILE")
            .help("Sets a custom config file")
            .takes_value(true))
        .arg(Arg::with_name("INPUT")
            .help("Sets the input file to use")
            .required(true)
            .index(1))
        .arg(Arg::with_name("v")
            .short("v")
            .multiple(true)
            .help("Sets the level of verbosity"))
        .subcommand(SubCommand::with_name("test")
            .about("controls testing features")
            .version("1.3")
            .author("Someone E. ")
            .arg(Arg::with_name("debug")
                .short("d")
                .help("print debug information verbosely")))
        .get_matches();

    // Gets a value for config if supplied by user, or defaults to "default.conf"
    let config = matches.value_of("config").unwrap_or("default.conf");
    println!("Value for config: {}", config);

    // Calling .unwrap() is safe here because "INPUT" is required (if "INPUT" wasn't
    // required we could have used an 'if let' to conditionally get the value)
    println!("Using input file: {}", matches.value_of("INPUT").unwrap());

    // Vary the output based on how many times the user used the "verbose" flag
    // (i.e. 'myprog -v -v -v' or 'myprog -vvv' vs 'myprog -v'
    match matches.occurrences_of("v") {
        0 => println!("No verbose info"),
        1 => println!("Some verbose info"),
        2 => println!("Tons of verbose info"),
        3 | _ => println!("Don't be crazy"),
    }

    // You can handle information about subcommands by requesting their matches by name
    // (as below), requesting just the name used, or both at the same time
    if let Some(matches) = matches.subcommand_matches("test") {
        if matches.is_present("debug") {
            println!("Printing debug info...");
        } else {
            println!("Printing normally...");
        }
    }

    // more program logic goes here...
}

// rsdemo.exe i.txt -v -v test -d

调用Win32 API

// winapi={ version = "", features = ["winuser"] }

#[cfg(windows)] extern crate winapi;
use std::io::Error;

#[cfg(windows)]
fn print_message(msg: &str) -> Result {
    use std::ffi::OsStr;
    use std::iter::once;
    use std::os::windows::ffi::OsStrExt;
    use std::ptr::null_mut;
    use winapi::um::winuser::{MB_OK, MessageBoxW};
    let wide: Vec = OsStr::new(msg).encode_wide().chain(once(0)).collect();
    let ret = unsafe {
        MessageBoxW(null_mut(), wide.as_ptr(), wide.as_ptr(), MB_OK)
    };
    if ret == 0 { Err(Error::last_os_error()) }
    else { Ok(ret) }
}
#[cfg(not(windows))]
fn print_message(msg: &str) -> Result<(), Error> {
    println!("{}", msg);
    Ok(())
}
fn main() {
    print_message("Hello, world!").unwrap();
}

json

let parsed = json::parse(r#"

{
    "code": 200,
    "success": true,
    "payload": {
        "features": [
            "awesome",
            "easyAPI",
            "lowLearningCurve"
        ]
    }
}

"#).unwrap();

let instantiated = object!{
    "code" => 200,
    "success" => true,
    "payload" => object!{
        "features" => array![
            "awesome",
            "easyAPI",
            "lowLearningCurve"
        ]
    }
};

assert_eq!(parsed, instantiated);

web

服务器端:
在目前状态下(2019-2-21)基本没有太多可选的,大部分web框架都依赖tokio执行,其中功能比较完善的是actix系列。

sql

mysql

#[macro_use]
extern crate mysql;
// ...

use mysql as my;

#[derive(Debug, PartialEq, Eq)]
struct Payment {
    customer_id: i32,
    amount: i32,
    account_name: Option,
}

fn main() {
    // See docs on the `OptsBuilder`'s methods for the list of options available via URL.
    let pool = my::Pool::new("mysql://root:password@localhost:3307/mysql").unwrap();

    // Let's create payment table.
    // Unwrap just to make sure no error happened.
    pool.prep_exec(r"CREATE TEMPORARY TABLE payment (
                         customer_id int not null,
                         amount int not null,
                         account_name text
                     )", ()).unwrap();

    let payments = vec![
        Payment { customer_id: 1, amount: 2, account_name: None },
        Payment { customer_id: 3, amount: 4, account_name: Some("foo".into()) },
        Payment { customer_id: 5, amount: 6, account_name: None },
        Payment { customer_id: 7, amount: 8, account_name: None },
        Payment { customer_id: 9, amount: 10, account_name: Some("bar".into()) },
    ];

    // Let's insert payments to the database
    // We will use into_iter() because we do not need to map Stmt to anything else.
    // Also we assume that no error happened in `prepare`.
    for mut stmt in pool.prepare(r"INSERT INTO payment
                                       (customer_id, amount, account_name)
                                   VALUES
                                       (:customer_id, :amount, :account_name)").into_iter() {
        for p in payments.iter() {
            // `execute` takes ownership of `params` so we pass account name by reference.
            // Unwrap each result just to make sure no errors happened.
            stmt.execute(params!{
                "customer_id" => p.customer_id,
                "amount" => p.amount,
                "account_name" => &p.account_name,
            }).unwrap();
        }
    }

    // Let's select payments from database
    let selected_payments: Vec =
    pool.prep_exec("SELECT customer_id, amount, account_name from payment", ())
    .map(|result| { // In this closure we will map `QueryResult` to `Vec`
        // `QueryResult` is iterator over `MyResult` so first call to `map`
        // will map each `MyResult` to contained `row` (no proper error handling)
        // and second call to `map` will map each `row` to `Payment`
        result.map(|x| x.unwrap()).map(|row| {
            // ⚠️ Note that from_row will panic if you don't follow your schema
            let (customer_id, amount, account_name) = my::from_row(row);
            Payment {
                customer_id: customer_id,
                amount: amount,
                account_name: account_name,
            }
        }).collect() // Collect payments so now `QueryResult` is mapped to `Vec`
    }).unwrap(); // Unwrap `Vec`

    // Now make sure that `payments` equals to `selected_payments`.
    // Mysql gives no guaranties on order of returned rows without `ORDER BY`
    // so assume we are lukky.
    assert_eq!(payments, selected_payments);
    println!("Yay!");
}

gui

比较建议使用Rust做后端+WebAssembly, 然后调用本地webview显示界面,提供特定功能。
参考:

  • https://github.com/huytd/kanban-app
  • https://crates.io/crates/web-view

azul
imgui
*hwgui

Servo RUST做的渲染引擎。

webassembly

https://rustwasm.github.io/book/game-of-life/introduction.html

image

image

extern crate image;

use image::GenericImageView;

fn main() {
    // Use the open function to load an image from a Path.
    // ```open```returns a `DynamicImage` on success.
    let img = image::open("test.jpg").unwrap();

    // The dimensions method returns the images width and height.
    println!("dimensions {:?}", img.dimensions());

    // The color method returns the image's `ColorType`.
    println!("{:?}", img.color());

    // Write the contents of this image to the Writer in PNG format.
    img.save("test.png").unwrap();
}

video stream

ffmpeg
ffmpeg-sys

通过Rc修改Struct

use std::rc::Rc;
use std::cell::{RefCell, Cell};

#[derive(Debug)]
struct MyArg {
    pub arg0: i32,
    pub arg1: Cell, // 不用mut, 直接set/replace
    pub arg2: RefCell, // 需要mut,然后修改
//    pub arg3: Cell, // Error, Cell要求必须实现Copy
}


fn main() {
    let a = Rc::new(MyArg {
        arg0: 0,
        arg1: Cell::new(10) ,
        arg2: RefCell::new("231".to_string()),
    });
    dbg!(&a);
    a.arg1.replace(20);
    // a.arg0 = 11; // can't do this
    a.arg2.borrow_mut().push_str("======");
    dbg!(&a);
}

Option/Result/Future

extern crate futures;

use futures::prelude::*;
use futures::Future;
use futures::future;
use std::result;
use std::io::Error;

// Option, Box, Future, Result

fn t1(a: i32) -> Option {
    if a > 10 {
        Some(a)
    } else {
        None
    }
}

fn t2(a: i32) -> Result {
    if a > 10 {
        Ok(a)
    } else {
        Err("a is too small".to_string())
    }
}

fn my_fn() -> Result> {
    Ok(100)
}

fn my_fut() -> impl Future> {
    future::ok(100)
}

fn main() {
    // Option 用法和解法
    println!("{:?}", t1(11).unwrap());
    println!("{:?}", t1(3).unwrap_or_else(|| -1));

    // Result 用法和解包
    // ? 操作符可以简化Result类型返回值的传递,最终解包还需手动
    println!("{:?}", t2(12).unwrap_or_default());
    let x = match t2(6) {
        Ok(v) => v,
        Err(_) => 0,
    };
    println!("{:?}", x);
    let x = t2(2).unwrap_or_else(|_| -1);
    println!("{:?}", x);

    // future 用法和解包
    // Rust 的 futures 其實就是 Results:也就是說你需要指定預期返回類型和錯誤類型。
    let f1 = my_fn();
    println!("{:?}", f1.unwrap_or(0));
    let f2 = my_fut();
    println!("{:?}", f2.wait().unwrap_or(0)); // wait 执行
    // 尴尬的是,目前的rust除了wait没有好的办法执行future。等待await出来后可能会好点。
    // 目前一般做法是使用pool跑,然后接收返回。建议使用tokio来做这些事。

    // Box 只是一个指针,可以直接访问。
}

传参&传值

// 传指针(引用)
fn fuc1( arg1: &mut i32 ){
    *arg1 = 2;
    println!("fuc1 {}", arg1);
}

// 传值
fn fuc2(mut arg1: i32){
    arg1 = 3;
    println!("fuc2 {}", arg1);
}

fn main() {
    let mut a = 10;
    fuc1(&mut a); // 匹配&mut i32类型,传递的是指针
    println!("{}", a); // 2
    fuc2(a);  // 匹配mut i32,但是mut是函数内arg1可变,仍然传值
    println!("{}", a); // 2
}

&mut误解

fn main() {
    let mut x = &mut 5_i32;
    println!("{:p} {}", x, x);
    let mut a = 10;
    x = &mut a; // x 指针赋值
    println!("{:p} {}", x, x);
    *x = 12;  // x指针指向的内存赋值
    println!("{:p} {}", x, x);
}
/*
0xebd031f7b4 5
0xebd031f82c 10
0xebd031f82c 12
*/

mut x 表示x变量是可变的,更改方式为x=&mut a;&mut 5_i32表示x指向一个i32指针,并且这个指针指向的内存区域存储的值可以更改,更改方法为*x = 12;

Copy&Clone

Copy内部没有方法,只是一个空的trait,必须有Clone才能实现Copy。给编译器采用,改变move行为的。如果一个结构体需要Copy,则其每个成员必须实现Copy.
Copy一般要求可以使用memcpy直接复制,而带有drop语义的一般不能实现Copy.

Clone需要实现具体的内容,内有2个方法。给编码者主动调用。任何类型都可实现Clone。

自动生成工具:#[derive(Clone,Copy)]

析构函数

在Rust中编写析构函数方法是实现impl std::ops::Drop

trait Drop{
    fn drop(&mut self);
}

资源管理

RUST的RAII通常在使用资源时,不需要手动关闭或者释放,依赖生存周期,自动完成。

use std::fs::File;
use std::io::Read;

fn main() {
    let f = File::open("/target/file/path");
    if f.is_err() {
        println!("file is not exist.");
        return;
    }
    let mut f = f.unwrap();
    let mut content = String::new();
    let result = f.read_to_string(&mut content);
    if result.is_err() {
        println!("read file error.");
        return;
    }
    println!("{}", result.unwrap());
}

生命周期提前结束可以使用std::mem::drop()方法主动结束。

除了move,clone,变量还可以被借用,&表示只读借用,&mut表示读写借用。借用指针也被称为引用ref。
借用指针与普通指针内部是一样的,只在语义上有区别。

共享不可变,可变不共享

  • &称为共享型指针,可多个存在
  • &mut称为独占型指针,只能存在一个

Rc是智能指针,只能共享访问存储内容,不能修改。
Cell具有内部可变性,不仅可以访问,还可以修改其存储内容。限制是通过get_mut()只能在Life time内存在一个可变访问。
RefCell同Cell,但是通过borrow/borrow_mut可以提供Ref/RefMut提供多个可读写对象使用。

Rust中提供了只读引用的类型有&、Rc、Arc等指针,它们可以提供alias。Rust中提供了内部可变性的类型有Cell、RefCell、Mutex、RwLock以及Atomic*系列类型等。这两类类型经常需要配合使用。
如果你需要把一个类型T封装到内部可变性类型中去,要怎样选择Cell和RefCell呢?
原则就是,如果你只需要整体性地存入、取出T,那么就选Cell。如果你需要有个可读写指针指向这个T修改它,那么就选RefCell。

Cow(Copy on write)当资源写入时才进行复制,否则是借用。

需要提醒大家注意的是,“取引用”操作符,如&、&mut,是不允许重载的。
因此,“取引用”和“解引用”并非对称互补关系。*&T的类型一定是T,而&*T的类型未必就是T。

你可能感兴趣的:(Rust)