最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Rust中類型轉(zhuǎn)換在錯誤處理中的應(yīng)用小結(jié)

 更新時間:2023年09月26日 11:39:20   作者:明天好,會的  
隨著項目的進(jìn)展,關(guān)于Rust的故事又翻開了新的一頁,今天來到了服務(wù)器端的開發(fā)場景,發(fā)現(xiàn)錯誤處理中的錯誤類型轉(zhuǎn)換有必要分享一下,對Rust錯誤處理相關(guān)知識感興趣的朋友一起看看吧

隨著項目的進(jìn)展,關(guān)于Rust的故事又翻開了新的一頁,今天來到了服務(wù)器端的開發(fā)場景,發(fā)現(xiàn)錯誤處理中的錯誤類型轉(zhuǎn)換有必要分享一下。

Rust抽象出來了Result<T,E>,T是返回值的類型,E是錯誤類型。只要函數(shù)的返回值的類型被定義為Resut<T,E>,那么作為開發(fā)人員就有責(zé)任來處理調(diào)用這個函數(shù)可能發(fā)生的錯誤。通過Result<T,E>,Rust其實給開發(fā)人員指明了一條錯誤處理的道路,使代碼更加健壯。

場景

  • 服務(wù)器端處理api請求的框架:Rocket
  • 服務(wù)器端處理數(shù)據(jù)持久化的框架:tokio_postgres

在api請求的框架中,我把返回類型定義成了 Result<T, rocket::response::status::Custom\<String>> ,即錯誤類型是 rocket::response::status::Custom\<String> 。

在tokio_postgres中,直接使用 tokio_postgres::error::Error 。

即如果要處理錯誤,就必須將 tokio_postgres::error::Error 轉(zhuǎn)換成 rocket::response::status::Custom\<String> 。那么我們從下面的原理開始,逐一領(lǐng)略Rust的錯誤處理方式,通過對比找到最合適的方式吧。

原理

對錯誤的處理,Rust有3種可選的方式

  • 使用match
  • 使用if let
  • 使用map_err

下面我結(jié)合場景,逐一演示各種方式是如何處理錯誤的。

下面的代碼中涉及到2個模塊(文件)。 /src/routes/notes.rs 是路由層,負(fù)責(zé)將api請求導(dǎo)向合適的service。 /src/services/note_books.rs 是service層,負(fù)責(zé)業(yè)務(wù)邏輯和數(shù)據(jù)持久化的處理。這里的邏輯也很簡單,就是route層調(diào)用service層,將數(shù)據(jù)寫入到數(shù)據(jù)庫中。

使用match

src/routes/notes.rs

#[post("/api/notes", format = "application/json", data = "<note>")]
pub async fn post_notes(note: Json<Note>) -> Result<(), rocket::response::status::Custom<String>> {
    insert_or_update_note(&note.into_inner()).await
}

/src/services/note_book.rs

pub async fn insert_or_update_note(
    note: &Note,
) -> Result<(), rocket::response::status::Custom<String>> {
    let (client, connection) = match connect(
        "host=localhost dbname=notes_db user=postgres port=5432",
        NoTls,
    )
    .await
    {
        Ok(res) => res,
        Err(err) => {
            return Err(rocket::response::status::Custom(
                rocket::http::Status::ExpectationFailed,
                format!("{}", err),
            ));
        }
    };
    ...
    match client
        .execute(
            "insert into notes (id, title, content) values($1, $2, $3);",
            &[&get_system_seconds(), &note.title, &note.content],
        )
        .await
    {
        Ok(res) => Ok(()),
        Err(err) => Err(rocket::response::status::Custom(
            rocket::http::Status::ExpectationFailed,
            format!("{}", err),
        )),
    }
}

通過上面的代碼我們可以讀出一下內(nèi)容:

  • 在service層定義了route層相同的錯誤類型
  • 在service層將持久層的錯誤轉(zhuǎn)換成了route層的錯誤類型
  • 使用match的代碼量還是比較大

使用if let

/src/services/note_book.rs

pub async fn insert_or_update_note(
    note: &Note,
) -> Result<(), rocket::response::status::Custom<String>> {
    if let Ok((client, connection)) = connect(
        "host=localhost dbname=notes_db user=postgres port=5432",
        NoTls,
    )
    .await
    {
        ...
        if let Ok(res) = client
            .execute(
                "insert into notes (id, title, content) values($1, $2, $3);",
                &[&get_system_seconds(), &note.title, &note.content],
            )
            .await
        {
            Ok(())
        } else {
            Err(rocket::response::status::Custom(
                rocket::http::Status::ExpectationFailed,
                format!("{}", "unknown error"),
            ))
        }
    } else {
        Err(rocket::response::status::Custom(
            rocket::http::Status::ExpectationFailed,
            format!("{}", "unknown error"),
        ))
    }
}

src/routes/notes.rs

#[post("/api/notes", format = "application/json", data = "<note>")]
pub async fn post_notes(note: Json<Note>) -> Result<(), rocket::response::status::Custom<String>> {
    insert_or_update_note(&note.into_inner()).await
}

使用了 if let ... ,代碼更加的別扭,并且在else分支中,拿不到具體的錯誤信息。

其實,不難看出,我們的目標(biāo)是將api的請求,經(jīng)過route層和service層,將數(shù)據(jù)寫入到數(shù)據(jù)中。但這其中的錯誤處理代碼的干擾就特別大,甚至要有邏輯嵌套現(xiàn)象。這種代碼的已經(jīng)離初衷比較遠(yuǎn)了,是否有更加簡潔的方式,使代碼能夠最大限度的還原邏輯本身,把錯誤處理的噪音降到最低呢?答案肯定是有的。那就是map_err

map_err

map_err是Result上的一個方法,專門用于錯誤的轉(zhuǎn)換。下面的代碼經(jīng)過了map_err的改寫,看上去是不是清爽了不少啊。/src/services/note_book.rs

pub async fn insert_or_update_note(
    note: &Note,
) -> Result<(), rocket::response::status::Custom<String>> {
    let (client, connection) = connect(
        "host=localhost dbname=notes_db user=postgres port=5432",
        NoTls,
    )
    .await
    .map_err(|err| {
        rocket::response::status::Custom(
            rocket::http::Status::ExpectationFailed,
            format!("{}", err),
        )
    })?;
    ...
    let _ = client
        .execute(
            "insert into notes (id, title, content) values($1, $2, $3);",
            &[&get_system_seconds(), &note.title, &note.content],
        )
        .await
        .map_err(|err| {
            rocket::response::status::Custom(
                rocket::http::Status::ExpectationFailed,
                format!("{}", err),
            )
        })?;
    Ok(())
}

src/routes/notes.rs

#[post("/api/notes", format = "application/json", data = "<note>")]
pub async fn post_notes(note: Json<Note>) -> Result<(), rocket::response::status::Custom<String>> {
    insert_or_update_note(&note.into_inner()).await
}

經(jīng)過map_err改寫后的代碼,代碼的邏輯流程基本上還原了邏輯本身,但是map_err要額外占4行代碼,且錯誤對象的初始化代碼存在重復(fù)。在實際的工程項目中,service層的處理函數(shù)可能是成百上千,如果再乘以4,那多出來的代碼量也不少啊,這會給后期的維護(hù)帶來不小的壓力。

那是否還有改進(jìn)的空間呢?答案是Yes。

Rust為我們提供了From<T> trait,用于類型轉(zhuǎn)換。它定義了從一種類型T到另一種類型Self的轉(zhuǎn)換方法。我覺得這是Rust語言設(shè)計亮點之一。

但是,Rust有一個顯示,即實現(xiàn)From<T> trait的結(jié)構(gòu),必須有一個在當(dāng)前的crate中,也就是說我們不能直接通過From<T>來實現(xiàn)從 tokio_postgres::error::Error rocket::response::status::Custom<String> 。也就是說下面的代碼編譯器會報錯。

impl From<tokio_postgres::Error> for rocket::response::status::Custom<String> {}

報錯如下:

32 | impl From<tokio_postgres::Error> for rocket::response::status::Custom<String> {}
   | ^^^^^---------------------------^^^^^----------------------------------------
   | |    |                               |
   | |    |                               `rocket::response::status::Custom` is not defined in the current crate
   | |    `tokio_postgres::Error` is not defined in the current crate
   | impl doesn't use only types from inside the current crate

因此,我們要定義一個類型 MyError 作為中間類型來轉(zhuǎn)換一下。/src/models.rs

pub struct MyError {
    pub message: String,
}
impl From<tokio_postgres::Error> for MyError {
    fn from(err: Error) -> Self {
        Self {
            message: format!("{}", err),
        }
    }
}
impl From<MyError> for rocket::response::status::Custom<String> {
    fn from(val: MyError) -> Self {
        status::Custom(Status::ExpectationFailed, val.message)
    }
}

/src/services/note_book.rs

pub async fn insert_or_update_note(
    note: &Note,
) -> Result<(), rocket::response::status::Custom<String>> {
    let (client, connection) = connect(
        "host=localhost dbname=notes_db user=postgres port=5432",
        NoTls,
    )
    .await
    .map_err(MyError::from)?;
    ...
    let _ = client
        .execute(
            "insert into notes (id, title, content) values($1, $2, $3);",
            &[&get_system_seconds(), &note.title, &note.content],
        )
        .await
        .map_err(MyError::from)?;
    Ok(())
}

src/routes/notes.rs

#[post("/api/notes", format = "application/json", data = "<note>")]
pub async fn post_notes(note: Json<Note>) -> Result<(), rocket::response::status::Custom<String>> {
    insert_or_update_note(&note.into_inner()).await
}

MyError rocket::response::status::Custom<String> 之間的轉(zhuǎn)換是隱式的,由編譯器來完成。因此我們的錯誤類型的轉(zhuǎn)換最終縮短為 map_err(|err|MyError::from(err)) ,再簡寫為 map_err(MyError::from)

關(guān)于錯誤處理中的類型轉(zhuǎn)換應(yīng)用解析就到這里。通過分析這個過程,我們可以看到,在設(shè)計模塊時,我們應(yīng)該確定一種錯誤類型,就像tokio_postgres庫一樣,只暴露了tokio_postgress::error::Error一種錯誤類型。這種設(shè)計既方便我們在設(shè)計模塊時處理錯誤轉(zhuǎn)換,也方便其我們的模塊在被調(diào)用時,其它代碼進(jìn)行錯誤處理。

到此這篇關(guān)于Rust中類型轉(zhuǎn)換在錯誤處理中的應(yīng)用解析的文章就介紹到這了,更多相關(guān)Rust錯誤處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 聊聊Rust 運算符

    聊聊Rust 運算符

    運算符 用于對數(shù)據(jù)執(zhí)行一些操作。被運算符執(zhí)行操作的數(shù)據(jù)我們稱之為操作數(shù)。下面通過本文給大家介紹Rust 運算符的相關(guān)知識,感興趣的朋友一起看看吧
    2021-11-11
  • Rust Atomics and Locks并發(fā)基礎(chǔ)理解

    Rust Atomics and Locks并發(fā)基礎(chǔ)理解

    這篇文章主要為大家介紹了Rust Atomics and Locks并發(fā)基礎(chǔ)理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • Rust 堆內(nèi)存指針 Box的實現(xiàn)示例

    Rust 堆內(nèi)存指針 Box的實現(xiàn)示例

    在 Rust 中,Box<T>是最基礎(chǔ)、最簡潔的智能指針,核心作用是將數(shù)據(jù)從棧內(nèi)存轉(zhuǎn)移到堆內(nèi)存,并通過獨占所有權(quán)機制管理堆內(nèi)存的分配與釋放,下面就來詳細(xì)的介紹一下,感興趣的可以了解一下
    2026-04-04
  • 用rust?寫一個jar包?class沖突檢測工具

    用rust?寫一個jar包?class沖突檢測工具

    這篇文章主要介紹了用rust?寫一個jar包?class沖突檢測工具?的相關(guān)資料,需要的朋友可以參考下
    2023-05-05
  • 如何使用Rust直接編譯單個的Solidity合約

    如何使用Rust直接編譯單個的Solidity合約

    本文介紹了如何使用Rust語言直接編譯Solidity智能合約,特別適用于沒有外部依賴或flatten后的合約,一般情況下,Solidity開發(fā)者使用Hardhat或Foundry框架,本文給大家介紹如何使用Rust直接編譯單個的Solidity合約,感興趣的朋友一起看看吧
    2024-09-09
  • 深入解析Rust中的智能指針

    深入解析Rust中的智能指針

    本文介紹了Rust中的智能指針,Box、Rc、Arc、RefCell、Mutex、RwLock對應(yīng)的功能、場景與常用方法,并提供了組合使用方式,以及Cow、Pin的使用方式,感興趣的朋友跟隨小編一起看看吧
    2025-10-10
  • 詳解Rust中的workspace

    詳解Rust中的workspace

    這篇文章主要向大家介紹Rust中的workspace,主要內(nèi)容包括基礎(chǔ)應(yīng)用、實用技巧、原理機制等方面,這個概念在Rust中是通用的,只不過maven換成了cargo,而模塊變成了crate,下面跟著小編通過一個例子給大家介紹下
    2022-03-03
  • 詳解Rust中的所有權(quán)機制

    詳解Rust中的所有權(quán)機制

    Rust?語言提供了跟其他系統(tǒng)編程語言相同的方式來控制你使用的內(nèi)存,但擁有數(shù)據(jù)所有者在離開作用域后自動清除其數(shù)據(jù)的功能意味著你無須額外編寫和調(diào)試相關(guān)的控制代碼,這篇文章主要介紹了Rust中的所有權(quán)機制,需要的朋友可以參考下
    2022-10-10
  • rust 一個日志緩存記錄的通用實現(xiàn)方法

    rust 一個日志緩存記錄的通用實現(xiàn)方法

    本文給出了一個通用的設(shè)計模式,通過建造者模式實例化記錄對象,可自定義格式化器將實例化后的記錄對象寫入到指定的緩存對象中,這篇文章主要介紹了rust 一個日志緩存記錄的通用實現(xiàn)方法,需要的朋友可以參考下
    2024-04-04
  • Tauri?打開本地文件踩坑分析解決

    Tauri?打開本地文件踩坑分析解決

    這篇文章主要為大家介紹了Tauri?打開本地文件踩坑分析解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04

最新評論

宣恩县| 庆城县| 平山县| 莫力| 平谷区| 越西县| 资阳市| 冷水江市| 红河县| 井冈山市| 北流市| 巨野县| 南陵县| 柯坪县| 桐柏县| 方正县| 普宁市| 哈尔滨市| 托里县| 杭州市| 禄丰县| 根河市| 郧西县| 通城县| 民县| 越西县| 富顺县| 读书| 承德县| 崇明县| 宜春市| 饶平县| 穆棱市| 行唐县| 平远县| 西华县| 河西区| 肃北| 赤峰市| 南投县| 横山县|