You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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"));

关键修改点说明

  1. 参数类型调整:将path: &str改为path: String,让函数获取路径的所有权,确保异步闭包可以安全持有该值满足'static要求。
  2. 依赖项传递:把path作为use_effect_with_deps的第二个参数传入,这样当路径变化时,副作用会重新执行,避免了原代码中依赖为空导致的逻辑问题。
  3. 错误处理优化:用match替代unwrap,增加错误日志输出,方便调试。
  4. 简化代码结构:移除不必要的嵌套clone,让代码更简洁。

内容的提问来源于stack exchange,提问作者Szilu Channel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 01:30:22