Rust parse() 浅析

代码

rust 复制代码
let user_id: i32 = match query.id.parse() {
    Ok(v) => v,
    Err(_) => return HttpResponse::Ok().json(""),
};

一、数据流转全景

复制代码
浏览器 URL                           actix-web 解析               parse() 转换
─────────────────────────────────    ──────────────────────      ─────────────────
GET /getuser?id=5                   query.id = "5"  (String)  →  5 (i32)
GET /getuser?id=abc                 query.id = "abc" (String) →  Err (解析失败)
GET /getuser?id=-10                 query.id = "-10" (String) →  -10 (i32)
GET /getuser?id=99999999999         query.id = "9999.."       →  Err (超出i32范围)

为什么需要 parse()?

URL 查询参数在 HTTP 协议中全是字符串 。浏览器发送 ?id=5 时,5 是一个字符串 "5",不是整数 5。而 data_manager 的查询函数需要 i32 类型:

rust 复制代码
pub fn get_user_by_id(&self, user_id: i32) -> Option<&User> {  // ← 要求 i32
    self.user_map.get(&user_id)
}

二、parse() 的工作原理

parse() 是 Rust 标准库中 str 类型的方法,定义在 FromStr trait 里:

rust 复制代码
// 标准库定义(简化版)
pub trait FromStr: Sized {
    type Err;
    fn from_str(s: &str) -> Result<Self, Self::Err>;
}

// parse 方法签名
fn parse<F: FromStr>(&self) -> Result<F, F::Err>;

核心特点:类型由目标变量推断

rust 复制代码
// parse() 返回什么类型,取决于你把它赋值给什么类型

let a: i32    = "42".parse().unwrap();     // → 42 (i32)
let b: f64    = "3.14".parse().unwrap();   // → 3.14 (f64)
let c: bool   = "true".parse().unwrap();   // → true (bool)
let d: usize  = "100".parse().unwrap();    // → 100 (usize)
let e: String = "hello".parse().unwrap();  // → "hello" (String,不是很有意义但合法)

在当前代码中,类型注解 let user_id: i32 告诉编译器 parse() 要解析成 i32


三、详细例子

例子 1:正常请求(parse 成功)

复制代码
浏览器请求:GET /getuser?id=42

actix_web 解析:
  URL 查询字符串 "id=42"
  ↓ #[derive(Deserialize)] 自动反序列化
  UserQuery { id: "42" }    ← id 是 String 类型

代码执行:
  query.id → "42"
  "42".parse::<i32>() → Ok(42)
  
  match Ok(42) {
      Ok(v) => v,              // ← 匹配这里,user_id = 42
      Err(_) => ...            //   不执行
  }

  ↓ dm.get_user_by_id(42)
  ↓ 在 user_map 中查找 key=42
  → 找到用户 → 返回 User JSON

例子 2:非法请求(parse 失败)

复制代码
浏览器请求:GET /getuser?id=hello

actix_web 解析:
  UserQuery { id: "hello" }

代码执行:
  query.id → "hello"
  "hello".parse::<i32>() → Err(ParseIntError { kind: InvalidDigit })
  
  match Err(ParseIntError) {
      Ok(v) => ...,                          //   不执行
      Err(_) => return HttpResponse::Ok().json(""),  // ← 匹配这里,直接返回空字符串
  }

  → 浏览器收到:""  (空的双引号 JSON)
  → 状态码:200 OK(但内容为空)

例子 3:溢出(parse 失败)

复制代码
浏览器请求:GET /getuser?id=2147483648

"2147483648".parse::<i32>() → Err(ParseIntError { kind: Overflow })
                              // i32 最大值是 2147483647

→ 同样返回空 JSON ""

例子 4:带符号、带空格(常见边界情况)

rust 复制代码
"  42  ".parse::<i32>()    // Ok(42)        ← parse 自动 trim 空白
"+42".parse::<i32>()       // Ok(42)        ← 正号合法
"-10".parse::<i32>()       // Ok(-10)       ← 负数合法
"4.2".parse::<i32>()       // Err           ← 小数点不合法(不是整数)
"".parse::<i32>()          // Err           ← 空字符串不合法

四、match 分支对比

rust 复制代码
let user_id: i32 = match query.id.parse() {
    Ok(v) => v,                                    // ✅ 解析成功 → 取值
    Err(_) => return HttpResponse::Ok().json(""),  // ❌ 解析失败 → 提前返回空JSON
};
parse() 结果 匹配分支 效果
Ok(5) Ok(v) => v user_id = 5,继续执行查询
Err(ParseIntError) Err(_) => return ... 函数直接返回,response body 为 ""

_ 是通配符,表示"不管错误具体是什么(非法字符、溢出、空字符串等),统一返回空 JSON"。


五、与其他语言对比

javascript 复制代码
// JavaScript:隐式转换,不报错
const id = parseInt("hello");  // → NaN(不报错,悄悄失败)
python 复制代码
# Python:抛出异常
id = int("hello")              # → ValueError: invalid literal for int()
rust 复制代码
// Rust:返回 Result,强制你处理失败情况
let id: i32 = "hello".parse(); // → Result<i32, ParseIntError>
// 编译器强制你必须 match / unwrap / ? ,不能忽略

六、完整调用链

复制代码
HTTP GET /getuser?id=5
  │
  ▼
actix_web 从 URL 提取查询参数
  │
  ▼
#[derive(Deserialize)] 自动将 id=5 → UserQuery { id: "5".to_string() }
  │
  ▼
web::Query<UserQuery> 提取到 query 变量
  │
  ▼
query.id.parse::<i32>()    ← 字符串 "5" → 整数 5
  │  ┌─ Ok(5)  → 继续
  │  └─ Err(_) → 返回 ""  (防御性编程)
  ▼
dm.get_user_by_id(5)       ← 在 HashMap<i32, User> 中查找
  │  ┌─ Some(user) → 序列化为 JSON 返回
  │  └─ None       → 返回 ""  (用户不存在)
  ▼
HttpResponse::Ok().json(user.to_json())
  │
  ▼
浏览器收到:
{
  "userId": 5,
  "ratings": [...],
  "averageRating": 3.8,
  ...
}
相关推荐
卷无止境1 小时前
别再被"乱码"吓到了:Python文件操作的门道
后端·python
神仙别闹1 小时前
基于C++实现(控制台)景区旅游管理系统
开发语言·c++·旅游
程序员爱钓鱼1 小时前
Rust Result 详解:可靠的错误处理机制
前端·后端·rust
程序员爱钓鱼1 小时前
GOPATH 与 Go Modules:Go 项目依赖管理的演变
后端·go
牛奔9 小时前
Go 如何打印调试深层或嵌套的结构体
开发语言·后端·golang
红烧大青虫10 小时前
HarmonyOS应用开发实战:小事记 - 数据迁移策略:RDB 表结构变更的版本号管理与 onUpgrade 回调
后端·华为·harmonyos·鸿蒙系统
geovindu10 小时前
go: Iterative Algorithms
开发语言·后端·算法·golang·迭代算法
学逆向的10 小时前
汇编——JCC指令
开发语言·汇编·网络安全
mayaairi11 小时前
JS循环语句深度解析:嵌套for、while与do...while
开发语言·前端·javascript