Skip to content

Rust

3 posts with the tag “Rust”

[Rust]thiserror错误处理

thiserror库提供了方便的派生宏,简化了Rust中自定义错误的创建和处理。

编程时,错误处理是无法规避且重要的地方,和Golang一样,Rust没有异常,只有错误。在使用Rust时,编译器会不断督促我们,处理哪些错误。这也使得程序 更加健壮,因为这使得代码运行部署前,就能发现错误并进行适当处理,基本不存在漏掉不处理的情况。

可恢复的(recoverable)和 不可恢复的(unrecoverable)错误

Section titled “可恢复的(recoverable)和 不可恢复的(unrecoverable)错误”

Rust 将错误分为两大类:可恢复的(recoverable)和 不可恢复的(unrecoverable)错误。使用Result<T,E>类型,处理可恢复的错误,panic!宏,在程序遇到 不可恢复的错误时直接中断程序的执行。

Rust 使用Result类型类处理可恢复的错误。

enum Result<T, E>
Ok(T),
Err(E),
}

Result 是一个枚举类型,TE是泛型类型参数,程序成功运行时返回的值T, 程序运行失败时返回的错误类型E。

Rust在标准库中提供了一个trait,sdt::error::Error,目前错误处理都是基于这个trait来进行,一个结构体/枚举如果实现了这个trait,那么我们认为,它就是一个错误类型。

/// Errors may provide cause information. [`Error::source()`] is generally
/// used when errors cross "abstraction boundaries". If one module must report
/// an error that is caused by an error from a lower-level module, it can allow
/// accessing that error via [`Error::source()`]. This makes it possible for the
/// high-level module to provide its own errors while also revealing some of the
/// implementation for debugging.
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "Error")]
#[rustc_has_incoherent_inherent_impls]
#[allow(multiple_supertrait_upcastable)]
pub trait Error: Debug + Display {
#[stable(feature = "error_source", since = "1.30.0")]
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
...
}

使用thiserror宏,可以使用极大简化,Cargo.toml中添加:

[dependencies]
thiserror = "1.0"

编译器支持:要求rustc 1.56+。

use thiserror::Error;
#[derive(Error, Debug)]
pub enum DataStoreError {
#[error("data store disconnected")]
Disconnect(#[from] io::Error),
#[error("the data for key `{0}` is not available")]
Redaction(String),
#[error("invalid header (expected {expected:?}, found {found:?})")]
InvalidHeader {
expected: String,
found: String,
},
#[error("unknown data store error")]
Unknown,
}

只要为struct或者每个成员提供#error("..."),那么就会自定义的错误生成Display实现,错误消息支持从错误中插入字段简写:

  • #[error(“{var}”)] ⟶ write!(”{}”, self.var)
  • #[error(“{0}”)] ⟶ write!(”{}”, self.0)
  • #[error(“{var:?}”)] ⟶ write!(”{:?}”, self.var)
  • #[error(“{0:?}”)] ⟶ write!(”{:?}”, self.0)

对于每一个含有#[from]属性的,都会生成一个From实现。

#[derive(Error, Debug)]
pub enum MyError {
Io {
#[from]
source: io::Error,
backtrace: Backtrace,
},
}

错误可以使用 error(transparent)sourceDisplay 方法直接转发到底层错误,而不添加额外的消息。这适用于需要“任何其他”变体的枚举。

use log::SetLoggerError;
#[derive(Debug, thiserror::Error)]
pub enum AppError {
#[error("{0} not found")]
NotFoundError(String),
#[error(transparent)]
RedisError(#[from] redis::RedisError),
}

#[from]属性标记意味着redis::RedisError会被AppError中的RedisError转换为AppError::RedisError

thiserror 库作者建议到,如果你是在开发库,涉及自己的专用错误类型,便于调用者发生错误时准确收到信息,则用thiserror ,而如果是开发应用,不关心返回的是什么错误类型,只是希望处理起来更简单则使用 anyhow 。

Vite 加载 wasm-pack 构建ES模块

学习rust 构建 WebAssembly 的官方教程,教程中是使用 webpack 作为 Javascript 打包器和开发服务器的(注意,使用 Rust 和 WebAssembly 不要求 webpack 或者其他类似工具,只是为了开发方便)。但不太喜欢 webpack 配置太过繁琐,而以前常用的 Parcel 对 WebAssembly 支持不太友好,而 Vite 正好可以满足需要,所以使用了Vite, 下面记录的配置。

在学习一段时间zig来构建wasm后,发现 zig 语言本身用起来的是很舒服,可也有些问题,主要还是语言还没有发布1.0版本,语言生态 环境还不够完善,就连 build.zig 构建 API 也在频繁变动中,Github.com 上的相关包在 zig 最新版本也无法顺利通过构建。其他的相关包,就更少了。 相比于此, rust 社区在2018开始就将 WebAssembly 作为,注重改善编程体验几个不同领域之一(其他还有Command Line、Networking, Embedded)。用 rust 来开发 WebAssembly 过程要舒适非常多,相关的生态也非常完备,社区活跃度也很高。

wasm-pack build 命令支持多个 --target 参数,这里选择的是 web, 输出可以在浏览器中作为ES模块原始导入的JS,其他几个参数可以相见官方文档Deploying Rust and WebAssembly

Vite 5.0 版本对 WebAssembly 的 ES 模块集成提案 尚未支持。 请使用 vite-plugin-wasm 或其他社区上的插件来处理。

将构建出来的本地wasm package 添加到 package.json 中:

"dependencies": {
"wasm-game-of-life": "file:../pkg"
},
Terminal window
yarn add --dev vite@5.0.0

根目录下添加配置文件vite.config.js:

import { defineConfig } from 'vite'
import wasm from "vite-plugin-wasm";
import topLevelAwait from "vite-plugin-top-level-await";
export default defineConfig({});
Terminal window
yarn add --dev vite-plugin-wasm
yarn add --dev vite-plugin-top-level-await
import wasm from "vite-plugin-wasm";
import topLevelAwait from "vite-plugin-top-level-await";
export default defineConfig({
plugins: [
wasm(),
topLevelAwait()
],
optimizeDeps: {
exclude: ["wasm-game-of-life"],
},
});

完成代码放在Github

Neovim调试Lua

使用 nvim-lsp-installer, 安装插件。

Plug 'neovim/nvim-lspconfig'
Plug 'williamboman/nvim-lsp-installer'

首先安装 Lua language server:

Terminal window
brew install lua-language-server

其次在 Neovim 中安装 sumneko_lua 配置:

: LspInstall sumneko_lua

查看 Lsp 详情:

: LspInstallInfo

image-20220217164510148

安装成功后,开始进行Lsp配置:

local runtime_path = vim.split(package.path, ';')
table.insert(runtime_path, "lua/?.lua")
table.insert(runtime_path, "lua/?/init.lua")
require'lspconfig'.sumneko_lua.setup {
settings = {
Lua = {
runtime = {
-- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim)
version = 'LuaJIT',
-- Setup your lua path
path = runtime_path,
},
diagnostics = {
-- Get the language server to recognize the `vim` global
globals = {'vim'},
},
workspace = {
-- Make the server aware of Neovim runtime files
library = vim.api.nvim_get_runtime_file("", true),
},
-- Do not send telemetry data containing a randomized but unique identifier
telemetry = {
enable = false,
},
},
},
}
:VimspectorInstall local-lua-debugger-vscode

添加配置.vimspector.json:

{
"configurations": {
"luajit": {
"adapter": "lua-local",
"filetypes": ["lua"],
"configuration": {
"request": "launch",
"type": "lua-local",
"cwd": "${workspaceFolder}",
"program": {
"lua": "luajit",
"file": "${file}"
}
}
}
}
}
call vimspector#Launch()

image-20220217170431671