欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

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

場(chǎng)景

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

在api請(qǐng)求的框架中,我把返回類(lèi)型定義成了 Result<T, rocket::response::status::Custom\<String>> ,即錯(cuò)誤類(lèi)型是 rocket::response::status::Custom\<String>

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

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

原理

對(duì)錯(cuò)誤的處理,Rust有3種可選的方式

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

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

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

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

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

  • 在service層定義了route層相同的錯(cuò)誤類(lèi)型
  • 在service層將持久層的錯(cuò)誤轉(zhuǎn)換成了route層的錯(cuò)誤類(lèi)型
  • 使用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分支中,拿不到具體的錯(cuò)誤信息。

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

map_err

map_err是Result上的一個(gè)方法,專(zhuān)門(mén)用于錯(cuò)誤的轉(zhuǎn)換。下面的代碼經(jīng)過(guò)了map_err的改寫(xiě),看上去是不是清爽了不少啊。/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)過(guò)map_err改寫(xiě)后的代碼,代碼的邏輯流程基本上還原了邏輯本身,但是map_err要額外占4行代碼,且錯(cuò)誤對(duì)象的初始化代碼存在重復(fù)。在實(shí)際的工程項(xiàng)目中,service層的處理函數(shù)可能是成百上千,如果再乘以4,那多出來(lái)的代碼量也不少啊,這會(huì)給后期的維護(hù)帶來(lái)不小的壓力。

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

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

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

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

報(bào)錯(cuò)如下:

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

因此,我們要定義一個(gè)類(lèi)型 MyError 作為中間類(lèi)型來(lái)轉(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)換是隱式的,由編譯器來(lái)完成。因此我們的錯(cuò)誤類(lèi)型的轉(zhuǎn)換最終縮短為 map_err(|err|MyError::from(err)) ,再簡(jiǎn)寫(xiě)為 map_err(MyError::from)。

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

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

相關(guān)文章

  • Rust?Postgres實(shí)例代碼

    Rust?Postgres實(shí)例代碼

    Rust Postgres是一個(gè)純Rust實(shí)現(xiàn)的PostgreSQL客戶端庫(kù),本文主要介紹了Rust?Postgres實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-05-05
  • 使用Rust開(kāi)發(fā)小游戲完成過(guò)程

    使用Rust開(kāi)發(fā)小游戲完成過(guò)程

    這篇文章主要介紹了使用Rust開(kāi)發(fā)小游戲的完整過(guò)程,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2023-11-11
  • Rust語(yǔ)言之結(jié)構(gòu)體和枚舉的用途與高級(jí)功能詳解

    Rust語(yǔ)言之結(jié)構(gòu)體和枚舉的用途與高級(jí)功能詳解

    Rust 是一門(mén)注重安全性和性能的現(xiàn)代編程語(yǔ)言,其中結(jié)構(gòu)體和枚舉是其強(qiáng)大的數(shù)據(jù)類(lèi)型之一,了解結(jié)構(gòu)體和枚舉的概念及其高級(jí)功能,將使你能夠更加靈活和高效地處理數(shù)據(jù),本文將深入探討 Rust 中的結(jié)構(gòu)體和枚舉,并介紹它們的用途和高級(jí)功能
    2023-10-10
  • Rust 中判斷兩個(gè) HashMap 是否相等

    Rust 中判斷兩個(gè) HashMap 是否相等

    在Rust標(biāo)準(zhǔn)庫(kù)中,HashMap 實(shí)現(xiàn)了 PartialEq 和 Eq trait,但是這些trait的實(shí)現(xiàn)是基于嚴(yán)格的結(jié)構(gòu)相等性,包括元素的順序,這篇文章主要介紹了Rust 中判斷兩個(gè) HashMap 是否相等,需要的朋友可以參考下
    2024-04-04
  • rust流程控制的具體使用

    rust流程控制的具體使用

    在Rust中,控制流包括條件語(yǔ)句、循環(huán)和匹配模式等,用于實(shí)現(xiàn)程序的邏輯和流程控制,本文就來(lái)詳細(xì)的介紹一下,感興趣的可以了解一下
    2023-12-12
  • Rust中GUI庫(kù)egui的簡(jiǎn)單應(yīng)用指南

    Rust中GUI庫(kù)egui的簡(jiǎn)單應(yīng)用指南

    egui(發(fā)音為“e-gooey”)是一個(gè)簡(jiǎn)單、快速且高度可移植的 Rust 即時(shí)模式 GUI 庫(kù),跨平臺(tái)、Rust原生,適合一些小工具和游戲引擎GUI,下面就跟隨小編一起來(lái)看看它的具體使用吧
    2024-03-03
  • Rust在寫(xiě)庫(kù)時(shí)實(shí)現(xiàn)緩存的操作方法

    Rust在寫(xiě)庫(kù)時(shí)實(shí)現(xiàn)緩存的操作方法

    Moka是一個(gè)用于Rust的高性能緩存庫(kù),它提供了多種類(lèi)型的緩存數(shù)據(jù)結(jié)構(gòu),包括哈希表、LRU(最近最少使用)緩存和?支持TTL(生存時(shí)間)緩存,這篇文章給大家介紹Rust在寫(xiě)庫(kù)時(shí)實(shí)現(xiàn)緩存的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2024-01-01
  • Rust中泛型的學(xué)習(xí)筆記

    Rust中泛型的學(xué)習(xí)筆記

    在Rust語(yǔ)言中,泛型是一種強(qiáng)大的工具,它允許我們編寫(xiě)可復(fù)用且靈活的代碼,本文主要介紹了Rust中泛型的學(xué)習(xí)筆記,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • Rust生成隨機(jī)數(shù)的項(xiàng)目實(shí)踐

    Rust生成隨機(jī)數(shù)的項(xiàng)目實(shí)踐

    Rust標(biāo)準(zhǔn)庫(kù)中并沒(méi)有隨機(jī)數(shù)生成器,常見(jiàn)的解決方案是使用rand包,本文主要介紹了Rust生成隨機(jī)數(shù)的項(xiàng)目實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • Rust 中的閉包之捕獲環(huán)境的匿名函數(shù)

    Rust 中的閉包之捕獲環(huán)境的匿名函數(shù)

    這篇文章介紹了Rust編程語(yǔ)言中的閉包,包括閉包的定義、使用、捕獲環(huán)境中的變量、類(lèi)型推斷與注解、與函數(shù)的比較以及實(shí)際應(yīng)用,閉包具有捕獲環(huán)境、類(lèi)型推斷和高效性等特性,是Rust中一個(gè)非常強(qiáng)大的工具,感興趣的朋友一起看看吧
    2025-02-02

最新評(píng)論