< 返回版块

rust 日报 babpstep 发表于 2022-09-27 21:00

Tags:cargo,axum,optimize

cargo careful:为你的代码提供更多安全保证

您是否知道标准库充满了用户永远看不到的有用检查?标准库中有很多断言,它们会做一些事情,比如检查 char::from_u32_unchecked 必须针对一个有效的 char、CStr::from_bytes_with_nul_unchecked 只能在没有内部 null 字节的情况下调用,copy 或者 copy_nonoverlapping 必须针对内存布局对齐的非 null 指针(非重叠)。但是,由 rustup 分发的常规标准库是在没有调试断言的情况下编译的,因此用户很难从这些额外检查中受益。

cargo careful 就是为了弥补这个差距,它在第一次调用时,会从源代码构建一个带有调试断言的标准库,然后使用该标准库运行您的程序或测试套件。

您可以通过 cargo install cargo-careful 快速安装,然后执行 cargo +nightly careful run/test 用于执行二进制 crate 或测试,并进行额外的调试检查。

需要注意的是,这自然会比常规调试或发布版本慢,但它比在 Miri 中执行程序要快得多,并且仍然有助于找到一些未定义的行为。当然,如果您想要更加彻底的检查,Miri 会是一个更好的选择,两者结合使用,体验更佳。

Axum 风格的函数参数示例

作者使用 Axum 时候,思考了这样一件事:Rust 是一个静态编译的语言,并且没有函数重载和可选参数这类特性,但是 Axum 中,get 函数却可以接收不同类型的函数指针,这是为什么呢?

let app = Router::new()
  .route("/users", get(get_users))
  .route("/products", get(get_product));

async fn get_users(Query(params): Query<Params>) -> impl IntoResponse {
    let users = /* ... */

    Json(users)
}

async fn get_product(State(db): State<Db>, Json(payload): Json<Payload>) -> String {
  let product = /* ... */

  product.to_string()
}

作者创建了一个仓库,动手实践,详细解释了其中的奥秘。

值得一提的是,Axum 当时也参考了 bevyquery system 设计,感兴趣的小伙伴不妨来学习一下。

编译器优化的思考

Rust 编译器背后为我们做了很多优化,但是,如果让你来实现,你会如何下手呢?典型的思路可能是:

  1. 找到可以应用特定优化方法的场景
  2. 通过分析,找到这种场景
  3. 应用你的优化方法

如果将很多编译优化方法结合起来,编译器的性能就能提升一大截。但是,这也绝非易事,在一个场景下实现编译优化,所要考虑的因素和需要的信息,远比你想象的要多。

本文作者就通过几个例子,解释了他对于编译优化背后的思考,相信你耐心看完,肯定有不少收获。

-- From 日报小组 RustPlumber

社区学习交流平台订阅:

评论区

写评论
c5soft 2022-09-29 08:55

Axum 风格的函数参数示例延伸

《Axum 风格的函数参数示例》充分展示了Rust的魔力,在Rust世界,一切皆对象,函数也是对象,对函数可以编写trait来扩展其功能。为便于操作github环境速度慢的学友阅读源代码,这里将原作者的代码适当修改贴出来供大家参考:

main.rs

use magic::{trigger, Context, Id, Param};

mod magic;

fn print_id(id: Id) {
    println!("id is {}", id.0);
}

fn print_param(Param(param): Param) {
    println!("param is {param}");
}

fn print_all(Param(param): Param, Id(id): Id) {
    println!("param is {param}, id is {id}");
}

fn print_all_switched(Id(id): Id, Param(param): Param) {
    println!("param is {param}, id is {id}");
}

fn print_3_arguments(Id(id): Id, c: Context, Param(param): Param) {
    println!("param is {param}, id is {id}, {:?}", c);
}

pub fn main() {
    let context = Context::new("magic".into(), 33);

    println!("context.call:");
    context.call(print_id);
    context.call(print_param);
    context.call(print_all);
    context.call(print_all_switched);
    context.call(print_3_arguments);

    let context = &context;
    println!("\ntrigger:");
    trigger(context, print_id);
    trigger(context, print_param);
    trigger(context, print_all);
    trigger(context, print_all_switched);
    trigger(context,print_3_arguments);
}

magic.rs

#[derive(Clone, Debug)]
pub struct Context {
    param: String,
    id: u32,
}

impl Context {
    pub fn new(param: String, id: u32) -> Self {
        Context { param, id }
    }
    pub fn call<T, H>(&self, handler: H)
    where
        H: Handler<T>,
    {
        handler.call(self)
    }
}

pub struct Param(pub String);

pub struct Id(pub u32);

pub trait FromContext {
    fn from_context(context: &Context) -> Self;
}

impl FromContext for Param {
    fn from_context(context: &Context) -> Self {
        Param(context.param.clone())
    }
}

impl FromContext for Id {
    fn from_context(context: &Context) -> Self {
        Id(context.id)
    }
}

impl FromContext for Context {
    fn from_context(context: &Context) -> Self {
        context.clone()
    }
}

pub trait Handler<T> {
    fn call(self, context: &Context);
}

#[allow(unused_parens)]
impl<T1, F> Handler<(T1)> for F
where
    F: Fn(T1),
    T1: FromContext,
{
    fn call(self, context: &Context) {
        self(T1::from_context(context));
    }
}

impl<T1, T2, F> Handler<(T1, T2)> for F
where
    F: Fn(T1, T2),
    T1: FromContext,
    T2: FromContext,
{
    fn call(self, context: &Context) {
        self(T1::from_context(context), T2::from_context(context));
    }
}

impl<T1, T2, T3, F> Handler<(T1, T2, T3)> for F
where
    F: Fn(T1, T2, T3),
    T1: FromContext,
    T2: FromContext,
    T3: FromContext,
{
    fn call(self, context: &Context) {
        self(
            T1::from_context(context),
            T2::from_context(context),
            T3::from_context(context),
        );
    }
}

pub fn trigger<T, H>(context: &Context, handler: H)
where
    H: Handler<T>,
{
    handler.call(context);
}

运行结果:

id is 33
param is magic
param is magic, id is 33
param is magic, id is 33
param is magic, id is 33, Context { param: "magic", id: 33 }

trigger:
id is 33
param is magic
param is magic, id is 33
param is magic, id is 33
param is magic, id is 33, Context { param: "magic", id: 33 }
1 共 1 条评论, 1 页