跳到主要内容

rust实用技巧

使用Cow<str> 作为返回类型

use std::borrow::Cow;

fn capitalize(name: &str) -> Cow<str> {
match name.chars().nth(0) {
Some(first_char) if first_char.is_uppercase() => {
// No allocation is necessary, as the string
// already starts with an uppercase char
Cow::Borrowed(name)
}
Some(first_char) => {
// An allocation is necessary, as the old string
// does not start with an uppercase char
let new_string: String = first_char.to_uppercase()
.chain(name.chars().skip(1))
.collect();

Cow::Owned(new_string)
},
None => Cow::Borrowed(name),
}
}

fn main() {
println!("{}", capitalize("bob")); // Allocation
println!("{}", capitalize("John")); // No allocation
}

注:写时复制(Copy on Write)技术是一种程序中的优化策略,多应用于读多写少的场景。主要思想是创建对象的时候不立即进行复制,而是先引用(借用)原有对象进行大量的读操作,只有进行到少量的写操作的时候,才进行复制操作,将原有对象复制后再写入。这样的好处是在读多写少的场景下,减少了复制操作,提高了性能

使用crossbeam 替代系统channel

crossbeam crate 为标准通道提供了强大的替代方案,支持 Select 操作、超时等。 类似于您在 Golang 和传统 Unix 套接字中开箱即用的内容。

use crossbeam_channel::{select, unbounded};
use std::time::Duration;

fn main() {
let (s1, r1) = unbounded::<i32>();
let (s2, r2) = unbounded::<i32>();
s1.send(10).unwrap();

select! {
recv(r1) -> msg => println!("r1 > {}", msg.unwrap()),
recv(r2) -> msg => println!("r2 > {}", msg.unwrap()),
default(Duration::from_millis(100)) => println!("timed out"),
}
}

注: 可以设置超市时间,可以预防程序一直阻塞

延迟运行 scopeguard

如果您来自 Golang,您可能会错过某些用例的延迟运算符(例如在使用原始指针或关闭套接字时释放内存)。

在 Rust 中(除了 RAII 模式),您可以使用 scopeguard crate 轻松实现清理逻辑。

#[macro_use(defer)] extern crate scopeguard;

fn main() {
println!("start");
{
// This action will run at the end of the current scope
defer! {
println!("defer");
}

println!("scope end");
}
println!("end");

// Output:
// start
// scope end
// defer
// end
}

闭包时使用 impl Trait

在可能的情况下,最好使用 impl Fn/FnOnce/FnMut 将闭包传递给函数(称为 impl Trait)而不是泛型,以保持签名干净。 对于 non-trivial 情况,您可能需要使用 Box<Fn()> 将闭包装箱,但请记住,您将支付额外的开销。

// Instead of this

fn setup_teardown_generic<A: FnOnce()>(action: A) {
println!("setting up...");

action();

println!("tearing down...")
}

// Use this

fn setup_teardown(action: impl FnOnce()) {
println!("setting up...");

action();

println!("tearing down...")
}

// As a note, this pattern is very useful inside tests
// to create/destroy resources.

fn main() {
setup_teardown(|| {
println!("Action!");
})

// Output:
// setting up...
// Action!
// tearing down...
}




使用thiserroranyhow进行通用的错误处理


use std::env::VarError;

use serde::{Deserialize, Serialize};
use thiserror::Error;

use super::downloader_error::DownLoaderError;
#[derive(Error, Debug, Serialize, Deserialize)]

pub enum CustomeErrors {
#[error("自定错误:{0}")]
CustomError(String),

#[error("sqlx异常:{0}")]
DatabaseError(String),
#[error("env读取异常:{0}")]
VarError(String),
}

impl From<sqlx::Error> for CustomeErrors {
fn from(e: sqlx::Error) -> Self {
Self::DatabaseError(format!("{:?}", e))
}
}

impl From<VarError> for CustomeErrors {
fn from(e: VarError) -> Self {
Self::VarError(format!("{:?}", e))
}
}

impl From<DownLoaderError> for CustomeErrors {
fn from(err: DownLoaderError) -> Self {
match err {
DownLoaderError::CustomError(e) => CustomeErrors::CustomError(e),
DownLoaderError::ReqwestError(e) => CustomeErrors::CustomError(e.to_string()),
DownLoaderError::IoError(e) => CustomeErrors::CustomError(e.to_string()),
DownLoaderError::Infallible(e) => CustomeErrors::CustomError(e.to_string()),
DownLoaderError::ParseIntError(e) => CustomeErrors::CustomError(e.to_string()),
}
}
}




使用 dbg!() 宏代替 println!()

调试时使用 dbg!() 宏而不是 println!()。 更少的代码,更多有用的信息。


fn main() {
let var1 = 2;

println!("{}", 2); // Output: 2
dbg!(var1); // Output: [src/main.rs:5] var1 = 2
dbg!(var1 * 2); // Output: [src/main.rs:6] var1 * 2 = 4
}

vec数组越界优化

[0..1]改为s.get(0..4)

fn vec_test() {
let mut s = Vec::new();
s.push(1);
s.push(1);
s.push(1);
let result = s.get(0..4);
println!("{:#?}",result);
}

String类型copy trait 相关内容

String类型没有实现copy trait 只有使用引用类型即&String

  • demo1

#[derive(Debug, PartialEq)]
struct Persion<'a>{
name:&'a str
}

#[derive(Debug, PartialEq)]
struct Persion2<'a>{
name:&'a String
}


fn get_name<'a>(p:&mut Persion , name:&'a str )->&'a str{
p.name="456";
name
}

fn get_name2 <'a> (p:&mut Persion2<'a> , name:&'a String ,sn_new:&'a String )->& 'a String {
p.name=sn_new;
name
}

fn main() {
let mut p = Persion{
name:"123"
};
let name=p.name;
let s = get_name(&mut p,&name);
println!("{s}");
println!("{:?}",p);
let sn= "xxx".to_string();
let mut p2 = Persion2{
name:&sn
};
let name2 = p2.name;
let sn_new="ttt".to_string();
let s2 = get_name2(&mut p2,&name2,&sn_new);

println!("{s2}");
println!("{:?}",p2);

}
  • demo2 注下面的x类型改为&str应该也是可以的
struct Foo<'a>{
x:&'a String,
}

impl <'a> Foo<'a>{
fn run(&mut self){


let a = self.x;
self.replace(a);
}
fn replace(&mut self,value:&'a String){
if self.x.as_str()!=value {
self.x = value
}
}
}

fn main(){
let x = "a".to_string();
Foo{
x:&x
}.run()
}

生命周期理解

假设你从某个地方得到一个r:&'c S,它意味着. r持有某个S的地址。 r指向的任何地址必须并且至少会存在'c. 变量r本身的存在时间不能超过'c. 生命周期标识'c表示的是S的作用域 如果A{value:&'c S} 则A结构体的生命期'a'满足 'a >='c A<'a,'a:'c>{value:&'c S}; 'a:'c表示 'a >= 'c

https://cheats.rs/

参考文章

[1]https://federicoterzi.com/blog/12-rust-tips-and-tricks-you-might-not-know-yet