V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Hantong
V2EX  ›  Rust

[Rust] 关于可变/不可变借用规则的求助(吐槽)一贴

  •  
  •   Hantong · 114 天前 · 1260 次点击
    这是一个创建于 114 天前的主题,其中的信息可能已经有所发展或是发生改变。

    背景: 正在给自己的小项目写 axum 全家桶的中间件.

    代码片段:

    /// Middleware for parsing and identifying Miku requests.
    pub async fn miku_identification(request: AxumRequest, next: Next) -> Result<AxumResponse, ServerErrorExt> {
        let (
            Parts {
                method,
                uri,
                version,
                headers,
                mut extensions,
                ..
            },
            body,
        ) = request.into_parts();
    
        let queries = Queries::from_uri(&uri).unwrap_or_default();
    
        // * Parse and identify APP type
        if let Some(app_type) = AppTypeExt::get_from(&queries)? {
            extensions.insert(app_type);
        }
    
        // * Parse and identify UserInfo
        if let Some(user_info) = UserInfo::get_from(&queries)? {
            extensions.insert(user_info);
        }
    
        let mut request = AxumRequest::new(body);
    
        *request.method_mut() = method;
        *request.uri_mut() = uri;
        *request.version_mut() = version;
        *request.headers_mut() = headers;
        *request.extensions_mut() = extensions;
    
        Ok(next.run(request).await)
    }
    

    相信各位都能看出来我在干嘛, 但是一眼看上去是相当别扭, 便问为什么不这么写:

    pub async fn miku_identification(mut request: AxumRequest, next: Next) -> Result<AxumResponse, ServerErrorExt> {
        let queries = Queries::from_uri(request.uri()).unwrap_or_default();
    
        // * Parse and identify APP type
        if let Some(app_type) = AppTypeExt::get_from(&queries)? {
            request.extensions_mut().insert(app_type);
        }
    
        // * Parse and identify UserInfo
        if let Some(user_info) = UserInfo::get_from(&queries)? {
            request.extensions_mut().insert(user_info);
        }
    
        Ok(next.run(request).await)
    }
    

    会 Rust 的一眼就能看出来, Queries::from_uri 的时候拿了不可变引用, 下面不能可变引用了.

    但是我寻思着我也没改动 uri 啊, 改的是 extension 啊, 烦闷, 把 request break down into parts 吧:

    /// Middleware for parsing and identifying Miku requests.
    pub async fn miku_identification(request: AxumRequest, next: Next) -> Result<AxumResponse, ServerErrorExt> {
        let (
            Parts {
                method,
                uri,
                version,
                headers,
                mut extensions
            },
            body,
        ) = request.into_parts();
    
        // ...
    
        let mut request = AxumRequest::from_parts(Parts {
                method,
                uri,
                version,
                headers,
                extensions
            },
            body
         );
    
        Ok(next.run(request).await)
    }
    

    各位肯定一眼看出有问题, 因为 Parts 下面还有个私有字段 _priv: (), 想半天没想明白干嘛的, 阻止下游私自构建 Parts? 自然也没提供方法从现有的 method 等组装... 恼火至极, 只能写出文首那种别扭的玩意.

    求问各位观众还有没有什么更好的写法.


    1 编:

    再看了一遍文档, 发现给了个 builder, 最别扭的问题解决了...

    2 条回复    2024-07-22 02:25:22 +08:00
    openmynet
        1
    openmynet  
       114 天前
    ```rust
    #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
    pub struct TokenParams {
    pub token: String,
    }
    impl TokenParams {
    pub fn from_uri(uri: &axum::http::Uri) -> Result<Self, impl IntoResponse> {
    uri.query()
    .ok_or((StatusCode::BAD_REQUEST, "缺失参数"))
    .and_then(|q| {
    serde_qs::from_str::<TokenParams>(q)
    .map_err(|_| (StatusCode::FORBIDDEN, "缺失必要参数"))
    })
    }
    }

    async fn authorize_demo(mut request: Request, next: Next) -> Result<Response, impl IntoResponse> {
    // 从 query 中获取
    let valid = TokenParams::from_uri(request.uri());

    match valid {
    Ok(valid) => {
    request.extensions_mut().insert(valid);
    Ok(next.run(request).await)
    }
    Err(err) => {
    return Err(err);
    }
    }
    }

    ```
    fakeshadow
        2
    fakeshadow  
       114 天前
    ```
    let (app_type , user_info) = {
    let queries = Queries::from_uri(request.uri()).unwrap_or_default();
    (AppTypeExt::get_from(&queries)?, UserInfo::get_from(&queries)?)
    };

    if let Some(app_type) = app_type {
    request.extensions_mut().insert(app_type);
    }

    if let Some(user_info) = user_info {
    request.extensions_mut().insert(user_info);
    }
    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1148 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 22:56 · PVG 06:56 · LAX 14:56 · JFK 17:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.