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

154 天前
 Hantong

背景: 正在给自己的小项目写 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, 最别扭的问题解决了...

1322 次点击
所在节点    Rust
2 条回复
openmynet
154 天前
```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
154 天前
```
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);
}
```

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1058981

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX