测试
单元测试
测试单一模块,且能够测试私有接口。
创建 tests
模块并使用 #[cfg(test)]
注解。一般测试模块与目标函数放置于同一个文件。
使用 cargo test
运行测试,cargo test name_slice
可运行指定测试
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
// 私有方法也可测试
fn bad_add(a: i32, b: i32) -> i32 {
a - b
}
#[cfg(test)]
mod tests {
// Note this useful idiom: importing names from outer (for mod tests) scope.
use super::*;
#[ignore = "这个属性能用于忽略测试"]
#[test]
fn test_add() {
assert_eq!(add(1, 2), 3);
}
// 测试函数也可以返回 Result
#[test]
fn test_bad_add() -> Result<(), String> {
if 2 == bad_add(1, 1) {
Ok(())
} else {
Err("the function is bad".to_owned())
}
}
#[test]
#[should_panic(expected="should panic")] // 当希望发生 panic 时使用
fn test_should_panic() {
panic!("should panic")
}
}
文档测试
文档注解支持commonMarkdown格式,其中的代码块在 cargo test 时也执行。
集成测试
cargo将集成测试放在与src平级的tests目录中,只能使用公开接口。
可以将所有测试都需要的初始设置放入一个函数,再引入test
例如:
// tests/common/mod.rs
pub fn setup() {
// some setup code, like creating required files/directories, starting
// servers, etc.
}
// importing common module.
mod common;
#[test]
fn test_add() {
// using common code.
common::setup();
assert_eq!(adder::add(3, 2), 5);
}
开发期依赖
引入后只在开始时有效
# standard crate data is left out
[dev-dependencies]
pretty_assertions = "1"
Vector
可变长的数组,一个vector对象有3个组成部分:指向数据的指针,长度,容量。
fn main() {
let mut v1: Vec<i32> = (0..10).collect();
let mut v2 = vec!["a", "b", "c"];
println!(
"{} {} {} {}", // 10 10 3 3
v1.len(),
v1.capacity(),
v2.len(),
v2.capacity()
);
for (_i, x) in v1.iter_mut().enumerate() {
*x *= 2;
}
v2.append(&mut vec!["d", "e", "f", "g"]);
println!(
"{} {} {:?}", // 7 7 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
v2.len(),
v2.capacity(),
v1
);
}
字符串
Rust中有两种字符串:String 和 &str。String是存储在堆上的字节vector,是不以null结尾的utf8字符。&str是指向utf8序列的切片,指向String。
std::str 和 std::string 专门用于处理 str 和 string 类型。
fn main() {
let s: &'static str = "this is a string data"; // 字面量分配在常量区,只读
println!("{:p} : {}", s.as_ptr(), s);
for word in s.split_whitespace().rev() {
println!("{:p} : {}", word.as_ptr(), word);
}
let S = String::from("this is a string data");
let ss = s.replace("string", "String");
println!("{:p}", S.as_str());
println!("{:p}", ss.as_str());
}
// print:
// 0x7ff6dbc656e8 : this is a string data
// 0x7ff6dbc656f9 : data
// 0x7ff6dbc656f2 : string
// 0x7ff6dbc656f0 : a
// 0x7ff6dbc656ed : is
// 0x7ff6dbc656e8 : this
// 0x2699c84f380
// 0x2699c8522c0
智能指针
Box
类似c++的 unique_ptr
智能指针,rust中对象默认分配栈上。可通过 Box 使其分配到堆上,当Box离开作用域时指针和内部对象都销毁。可以通过 * 操作符解引用,直接获得内部对象。
#[derive(Debug, Clone, Copy)]
struct Point {
x: f64,
y: f64,
}
fn main() {
let p = Point { x: 1.0, y: 2.0 };
let bp = Box::new(p);
println!(
"{} {} {}", // 16 8 16
std::mem::size_of_val(&p),
std::mem::size_of_val(&bp),
std::mem::size_of_val(&*bp)
);
}
Rc
Rc(Reference Counting)是对内部对象的引用计数指针,类似c++的 shared_ptr 和 weak_ptr。
内部分为强引用和弱引用,弱引用用于防止循环引用。
fn main() {
let s = "xxx".to_string();
let s1 = Rc::new(s);
println!("{} {}", Rc::strong_count(&s1), Rc::weak_count(&s1)); // 1 0
{
let _s2 = Rc::clone(&s1);
println!("{} {}", Rc::strong_count(&s1), Rc::weak_count(&s1)); // 2 0
}
{
println!("{} {}", Rc::strong_count(&s1), Rc::weak_count(&s1)); // 1 0
let s3 = Rc::downgrade(&s1);
println!("{} {}", Rc::strong_count(&s1), Rc::weak_count(&s1)); // 1 1
let _s4 = s3.upgrade();
println!("{} {}", Rc::strong_count(&s1), Rc::weak_count(&s1)); // 2 1
}
println!("{} {}", Rc::strong_count(&s1), Rc::weak_count(&s1)); // 1 0
}
Arc
Arc(Atomically Reference Counted)用于多线程间共享所有权。
use std::{sync::Arc, thread};
fn main() {
let s = Arc::new("string");
let mut join_vec = vec![];
for _ in 0..10 {
let ss = Arc::clone(&s);
let join = thread::spawn(move || {
println!("{:?} {:?} {:p}", ss, Arc::strong_count(&ss), ss.as_ptr());
});
join_vec.push(join);
}
join_vec.into_iter().for_each(|j| {
let _ = j.join();
});
}
// "string" 5 0x7ff7f8e29f38
// "string" 6 0x7ff7f8e29f38
// "string" 9 0x7ff7f8e29f38
// "string" 11 0x7ff7f8e29f38
// "string" 10 0x7ff7f8e29f38
// "string" 10 0x7ff7f8e29f38
// "string" 9 0x7ff7f8e29f38
// "string" 9 0x7ff7f8e29f38
// "string" 8 0x7ff7f8e29f38
// "string" 8 0x7ff7f8e29f38
多线程
一个简单 map-reduce 程序
MapReduce
use std::{collections::HashMap, thread};
fn main() {
let data = "86967897737416471853297327050364959
11861322575564723963297542624962850
70856234701860851907960690014725639
38397966707106094172783238747669219
52380795257888236525459303330302837
58495327135744041048897885734297812
69920216438980873548808413720956532
16278424637452589860345374828574668";
let mut join_vec = vec![];
let chunked_data = data.split_whitespace();
for (i, segment) in chunked_data.enumerate() {
join_vec.push(thread::spawn(move || -> HashMap<u32, i32> {
let mut map = HashMap::new();
segment.chars().for_each(|c| {
let num = c.to_digit(10).expect("should be digit");
match map.get_key_value(&num) {
Some((&k, &v)) => {map.insert(k, v + 1);}
None => {map.insert(num, 1);}
}
});
println!("thread {i} get result: {:?}", map);
map
}));
}
let mut final_result = HashMap::new();
join_vec.into_iter().for_each(|res| {
match res.join() {
Ok(map) => {
map.iter().for_each(|(&k, &v)| {
match final_result.get(&k) {
Some(old_result) => {
final_result.insert(k, v + old_result);
},
None => {
final_result.insert(k, v);
},
}
});
},
Err(err) => {
println!("thread fail: {:?}", err);
},
}
});
println!("final result: {:#?}", final_result);
}
标准库的 channel 支持多sender,1个receiver。第三方库 crossbeam_channel 支持多个生产者和消费者。
use std::{sync::mpsc, thread};
static THREAD_NUM: i32 = 3;
fn main() {
let (sender, receiver) = mpsc::channel();
let mut join_vec = vec![];
for i in 0..THREAD_NUM {
let sender2 = sender.clone();
let join = thread::spawn(move || {
sender2.send(i).unwrap();
println!("thread {i} send: {i}");
});
join_vec.push(join);
}
for _ in 0..THREAD_NUM {
receiver.recv().map_or_else(
|err| println!("err: {err}"),
|recv| println!("recv: {recv}"),
);
}
for j in join_vec {
j.join().expect("the child thread panic");
}
}
IO
*nix 和 windows 内部使用不同的格式保存字符串,所以Path对应的路径表示不是uft8格式字符串,而是依赖平台的 OsStr。Path 依赖 OsStr 创建,是不可变的。PathBuf 是可变的,与 Path 相关的。关系类似String 和 str。Path 转换为字符串可能失败,但一定可转换为 OsStr。
use std::path::Path;
fn main() {
let cur_path = Path::new(".");
let mut new_path = cur_path.join("a").join("b");
new_path.push("c");
new_path.push("file.txt");
println!("{:?} {:?}", cur_path, new_path);
}
文件操作:
use std::{
fs::{self, File},
io::{self, Write, BufRead},
path::Path,
};
fn main() -> io::Result<()> {
let file_path = Path::new("D:/tmp.txt");
if file_path.try_exists()? {
fs::remove_file(file_path)?;
}
{
let mut f = File::create(file_path)?;
f.write(b"123456\nabcdef")?;
}
let file = match File::open(file_path) {
// 当 File 对象超出作用域时,自动释放资源
Ok(file) => file,
Err(err) => panic!("open file failed: {}", err),
};
// file.read_buf()
for ele in io::BufReader::new(file).lines() {
if let Ok(line) = ele {
println!("{line}");
}
}
Ok(())
}
子进程
use std::{io, process::Command};
fn main() -> io::Result<()> {
let output = Command::new("rustc").args(["--version"]).output()?;
if output.status.success() {
let s = String::from_utf8_lossy(&output.stdout);
println!("successded:\n{}", s);
} else {
let s = String::from_utf8_lossy(&output.stderr);
println!("fail:\n{}", s);
}
Ok(())
}
标签:join,string,语法,let,fn,Rc,println,rust
From: https://www.cnblogs.com/zhh567/p/17224259.html