• 【译】12 条你可能还不知道的 Rust 提示和技巧


    Rust 是一种伟大的编程语言: 可靠、快速、令人愉快,但也相当复杂。在过去的两年里,我一直在专业和业余项目(比如 Espanso)中使用它。在那段时间里,我偶然发现了许多有用的模式和 crate,我希望在刚开始学习它的时候就知道它。

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

    有时你需要编写接受字符串片段(&str)并有条件地返回其修改版本或原始版本的方法。对于这些情况,你可以使用 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
    }
    

    使用 Crossbeam channel 而非标准库中的 channel

    crossbeam crate 提供了一个强大的替代标准 channel,支持 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"),
        }
    }
    

    Golang 风格的 Scopeguard 操作符

    如果你从 Golang 转过来,你可能会错过某些用例的 “defer” 操作符(比如使用原始指针时释放内存或关闭套接字)。

    在 Rust (除了 RAII 模式之外)中,你可以使用作用域保护(scopeguard)框来轻松实现“清理”逻辑。

    #[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
    }
    

    使用 Cargo-make 进行打包

    构建脚本对很多场景都很有用,但通常不适用于打包。我最喜欢的解决方案是 sagiegurariCargo Make,一个基于 Rust 的任务运行器和构建工具。

    自定义并链接 panic 处理程序

    应急处理程序(Panic handlers)(也称 hooks)可以被重写和链接,这在为应用程序设置自定义错误报告和日志记录时特别有用。

    use std::panic::{set_hook, take_hook};
    
    fn main() {
        let prev_hook = take_hook();
    
        set_hook(Box::new(move |panic| {
            println!("custom logging logic {}", panic);
    
            prev_hook(panic);
        }));
    
        let prev_hook = take_hook();
    
        set_hook(Box::new(move |panic| {
            println!("custom error reporting logic {}", panic);
    
            prev_hook(panic);
        }));
    
        panic!("test")
    
        // Output:
        // custom error reporting logic panicked at 'test', src/main.rs:20:5
        // custom logging logic panicked at 'test', src/main.rs:20:5
    }
    

    使用 VSCode 中的 Rust Analyzer 插件开发

    matklad 写的 Rust Analyzer 扩展是显着优于“官方” 的 Rust 插件之一。不幸的是,它仍然作为第二个查询结果出现在扩展市场上,误导了很多初学者。

    使用闭包时使用 impl Trait

    如果可能的话,倾向于将闭包传递给一个函数(称为 impl Trait) ,而不是通用函数,以保持签名的干净。对于非常见的情况,你可能需要用 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...
    }
    

    使用 VSCode 时,配置保存时启用 Clippy

    如果你正在使用 VSCode + RA,我强烈建议进入设置 > RA > 检查保存: 命令和设置“ clippy”作为新的默认值,而不是“检查”。同样的用户体验,更好的警告。

    在常用错误处理中使用“thiserror”和“anyway”

    使用 thiserror 和邋遢板条箱处理惯用错误。当消费者需要根据错误有条件地采取行动时,你应该使用此错误,否则无论如何。一个很好的指导方针是“对库和应用程序使用
    这个错误”。

    使用 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
    }
    

    include_str!() 和 include_bytes!() 宏

    使用 include_str!()include_bytes!() 宏在编译时读取文件的内容并将其存储在 const 中。有助于避免混乱使用多行字符串文本。

    // Both of these files are read at *compile time*
    const FILE_CONTENT: &str = include_str!("./path/to/the/file.txt");
    const BINARY_FILE_CONTENT: &[u8] = include_bytes!("./path/to/image.png");
    
    fn main() {
        println!("{}", FILE_CONTENT);  // Output: file content as string
    }
    

    与 c/c++ 代码的集成

    如果你需要将 c/c++ 代码与 Rust 集成在一起,那么 cc crate 和适当的构建脚本可以让你更加得心应手。例如,我使用它们将流行的 c++ gui 框架 wxWidgets 与我的项目 Espanso 集成(参见构建脚本

    // Inside the build script (build.rs)
    
    fn main() {
      println!("cargo:rerun-if-changed=src/native.c");
      println!("cargo:rerun-if-changed=src/native.h");
      cc::Build::new()
        .include("src/native.h")
        .file("src/native.c")
        .compile("nativemodule");
      println!("cargo:rustc-link-lib=static=nativemodule");
    }
    
    // Then, in another Rust file (for example, ffi.rs)
    
    #[link(name = "nativemodule", kind = "static")]
    extern "C" {
      pub fn your_cool_c_module();
    }
    

    谢谢阅读!如果你喜欢这些话题,请务必在 TwitterYouTube 上关注我,随时了解最新的文章和视频。

  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    http 请求code状态码
    js 跳转链接
    a标签-伪类
    在linux中如何解压.tgz
    dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.63.dylib
    nginx: [warn] conflicting server name "localhost" on 0.0.0.0:80, ignored
    微信小程序实现左滑删除效果(原生/uni-app)
  • 原文地址:https://www.cnblogs.com/ishenghuo/p/15907273.html
Copyright © 2020-2023  润新知