如何在Rust的Yew框架中实现泛型API数据获取函数?
解决Yew框架中泛型数据获取函数的生命周期问题
问题背景
在Yew项目中,想要基于模型实现泛型的后端API数据获取函数,基础的非泛型代码可以正常运行,但泛型版本遇到了生命周期报错。
模型代码
use serde::{Serialize, Deserialize}; use yew::Properties; #[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Properties, Default)] pub struct User { pub id: i64, pub name: String, pub email: String, pub password: String, }
可正常运行的非泛型代码
let users: UseStateHandle<Vec<User>> = use_state(|| vec![]); { let users = users.clone(); use_effect_with_deps(move |_| { let users = users.clone(); wasm_bindgen_futures::spawn_local(async move { let fetched_users: Vec<User> = Request::get(&format!("{BACKEND}/users/all")) .send() .await .unwrap() .json() .await .unwrap(); users.set(fetched_users); }); || () }, ()); }
尝试的泛型函数及调用代码
泛型函数:
fn fetch_data_try<T: serde::de::DeserializeOwned + 'static>(handle: &UseStateHandle<T>, path: &str) -> UseStateHandle<T>{ let handle = handle.clone(); let path = path.clone(); { let handle = handle.clone(); use_effect_with_deps(move |_| { let handle = handle.clone(); wasm_bindgen_futures::spawn_local(async move { let fetched_data: T = Request::get(path) .send() .await .unwrap() .json() .await .unwrap(); handle.set(fetched_data); }); || () }, ()); } handle }
调用代码:
let users: UseStateHandle<Vec<User>> = use_state(|| vec![]); let users = fetch_data_try::<Vec<User>>(&users, &format!("{BACKEND}/users/all"));
报错信息
error[E0521]: borrowed data escapes outside of function --> frontend/src/app.rs:99:13 | 92 | fn fetch_data_try<T: serde::de::DeserializeOwned + 'static>(handle: &UseStateHandle<T>, path: &str) -> UseStateHandle<T>{ | ---- - let's call the lifetime of this reference `'1` | | | `path` is a reference that is only valid in the function body ... 99 | / wasm_bindgen_futures::spawn_local(async move { 100 | | let fetched_data: T = Request::get(path) 101 | | .send() 102 | | .await ... | 107 | | handle.set(fetched_data); 108 | | }); | | ^ | | | | |______________`path` escapes the function body here | argument requires that `'1` must outlive `'static`
解决方案
报错原因分析
报错核心是生命周期不匹配:spawn_local要求传入的异步闭包必须满足'static生命周期,但原函数中path是引用类型(&str),其生命周期仅局限于函数内部,无法满足闭包的'static要求。虽然代码里写了path.clone(),但&str的clone仍然是引用,并没有真正获取所有权。
修改后的泛型函数
将path参数改为String类型,确保获取所有权,同时调整use_effect_with_deps的依赖项,把path作为依赖传入,确保副作用在路径变化时重新触发:
use yew::prelude::*; use wasm_bindgen_futures::spawn_local; use yew::services::fetch::Request; fn fetch_data<T: serde::de::DeserializeOwned + 'static>(handle: UseStateHandle<T>, path: String) -> UseStateHandle<T> { let handle_clone = handle.clone(); use_effect_with_deps(move |path| { let handle = handle_clone.clone(); spawn_local(async move { match Request::get(path) .send() .await .and_then(|resp| resp.json().await) { Ok(data) => handle.set(data), Err(e) => log::error!("Failed to fetch data: {:?}", e), } }); || () }, path); handle }
修改后的调用代码
调用时直接传入String类型的路径,不需要传引用:
let users: UseStateHandle<Vec<User>> = use_state(|| vec![]); let users = fetch_data(users, format!("{BACKEND}/users/all"));
关键修改点说明
- 参数类型调整:将
path: &str改为path: String,让函数获取路径的所有权,确保异步闭包可以安全持有该值满足'static要求。 - 依赖项传递:把
path作为use_effect_with_deps的第二个参数传入,这样当路径变化时,副作用会重新执行,避免了原代码中依赖为空导致的逻辑问题。 - 错误处理优化:用
match替代unwrap,增加错误日志输出,方便调试。 - 简化代码结构:移除不必要的嵌套
clone,让代码更简洁。
内容的提问来源于stack exchange,提问作者Szilu Channel
相关产品推荐
相关产品推荐

