我如何通用化 Yew Web 请求
How do I genericize Yew web request
我正在尝试弄清楚如何从 yew 示例中生成此 Web 请求代码,该示例对枚举的反序列化类型和变体具有通用性。
// Deserialize type
#[derive(Debug, Deserialize)]
pub struct TagsResponse {
tags: Vec<String>,
}
// Enum variants
pub enum Msg {
TagsLoaded(Result<TagsResponse, Error>),
TagsLoadError,
}
// Working non-generic inline code
let callback = model.link.send_back(
// want to make TagsResponse generic ⤵
move |response: Response<Json<Result<TagsResponse, Error>>>| {
let (meta, Json(data)) = response.into_parts();
if meta.status.is_success() {
// ↓ and be able to pass in an enum value
Msg::TagsLoaded(data)
} else {
// ↓ and be able to pass in an enum value
Msg::TagsLoadError
}
},
);
let request = Request::get(format!("{}{}", API_ULR, "tags"))
.body(Nothing)
.unwrap();
let task = model.fetch_service.fetch(request, callback);
model.fetch_task.push(task);
据我所知,这似乎很接近,但我已经进入了一种跟随编译器的循环:
fn remote_get<T: 'static>(
fetch_service: &mut FetchService,
link: &mut ComponentLink<Model>,
success_msg: fn(Result<T, Error>) -> Msg,
error_msg: Msg,
) -> FetchTask
where
for<'de> T: serde::Deserialize<'de>,
{
let callback = link.send_back(move |response: Response<Json<Result<T, Error>>>| {
let (meta, Json(data)) = response.into_parts();
if meta.status.is_success() {
success_msg(data)
} else {
error_msg
}
});
let request = Request::get(format!("{}{}", API_ULR, "articles?limit=10&offset=0"))
.body(Nothing)
.unwrap();
fetch_service.fetch(request, callback)
}
与调用站点:
let task = remote_get(
&mut self.fetch_service,
&mut self.link,
Msg::TagsLoaded,
Msg::TagsLoadError,
);
self.fetch_task.push(task);
产生:
|
598 | error_msg: Msg,
| --------- captured outer variable
...
608 | error_msg
| ^^^^^^^^^ cannot move out of captured variable in an `Fn` closure
奇怪的是,如果我从参数列表中删除 error_msg 并简单地硬编码 Msg::TagsLoadError
它会编译,但请求不会 运行。 ♂️
ComponentLink::send_back()
期望 Fn
关闭。但是,您的闭包正在消耗一个捕获的变量,即 error_msg
,因此它只能被调用一次。这使得你的闭包实现 FnOnce
而不是 Fn
,所以它不能在那里使用。
更简单的查看方式是:
struct Foo;
fn call(f: impl Fn() -> Foo) {}
fn test(x: Foo) {
let cb = move || x;
call(cb);
}
完整的错误信息比较清楚:
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/lib.rs:6:14
|
6 | let cb = move || x;
| ^^^^^^^^-
| | |
| | closure is `FnOnce` because it moves the variable `x` out of its environment
| this closure implements `FnOnce`, not `Fn`
7 | call(cb);
| ---- the requirement to implement `Fn` derives from here
这是有道理的;如果你多次写 call(cb)
会发生什么?请记住,Foo
不可复制也不可克隆。
准确地说,最简单的解决方案是使您的类型可克隆,以便可以重复使用:
let cb = move || {
x.clone()
};
而且有效!
如果您不想要克隆的成本,您可以添加一些解决方法,例如传递一个 returns 错误的函数或某种引用计数指针。例如:
struct Foo;
fn call(f: impl Fn() -> Foo) {}
fn test(build_x: impl Fn() -> Foo) {
let cb = move || build_x();
call(cb);
}
这个能用是因为build_x
是一个Fn
,不是FnOnce
,所以用的时候不消耗,就是想调用多少次就调用多少次.
另一个没有回调的解决方法是使用 Option
并使用 Option::take
使用它。这会将其替换为 None
,并且从借用检查器的角度来看,该值继续存在。但是你需要一个 RefCell
因为否则你会改变一个捕获的变量并将你的闭包转换成一个 FnMut
.
use std::cell::RefCell;
struct Foo;
fn call(f: impl Fn() -> Foo) {}
fn test(x: Foo) {
let ox = RefCell::new(Some(x));
let cb = move || ox.borrow_mut().take().unwrap();
call(cb);
}
更新到最后一个选项
不要使用 RefCell
,而简单的 Cell
就可以。而 Cell
有一个 take
成员函数,使这段代码更简单:
use std::cell::Cell;
struct Foo;
fn call(f: impl Fn() -> Foo) {}
fn test(x: Foo) {
let ox = Cell::new(Some(x));
let cb = move || ox.take().unwrap();
call(cb);
}
我正在尝试弄清楚如何从 yew 示例中生成此 Web 请求代码,该示例对枚举的反序列化类型和变体具有通用性。
// Deserialize type
#[derive(Debug, Deserialize)]
pub struct TagsResponse {
tags: Vec<String>,
}
// Enum variants
pub enum Msg {
TagsLoaded(Result<TagsResponse, Error>),
TagsLoadError,
}
// Working non-generic inline code
let callback = model.link.send_back(
// want to make TagsResponse generic ⤵
move |response: Response<Json<Result<TagsResponse, Error>>>| {
let (meta, Json(data)) = response.into_parts();
if meta.status.is_success() {
// ↓ and be able to pass in an enum value
Msg::TagsLoaded(data)
} else {
// ↓ and be able to pass in an enum value
Msg::TagsLoadError
}
},
);
let request = Request::get(format!("{}{}", API_ULR, "tags"))
.body(Nothing)
.unwrap();
let task = model.fetch_service.fetch(request, callback);
model.fetch_task.push(task);
据我所知,这似乎很接近,但我已经进入了一种跟随编译器的循环:
fn remote_get<T: 'static>(
fetch_service: &mut FetchService,
link: &mut ComponentLink<Model>,
success_msg: fn(Result<T, Error>) -> Msg,
error_msg: Msg,
) -> FetchTask
where
for<'de> T: serde::Deserialize<'de>,
{
let callback = link.send_back(move |response: Response<Json<Result<T, Error>>>| {
let (meta, Json(data)) = response.into_parts();
if meta.status.is_success() {
success_msg(data)
} else {
error_msg
}
});
let request = Request::get(format!("{}{}", API_ULR, "articles?limit=10&offset=0"))
.body(Nothing)
.unwrap();
fetch_service.fetch(request, callback)
}
与调用站点:
let task = remote_get(
&mut self.fetch_service,
&mut self.link,
Msg::TagsLoaded,
Msg::TagsLoadError,
);
self.fetch_task.push(task);
产生:
|
598 | error_msg: Msg,
| --------- captured outer variable
...
608 | error_msg
| ^^^^^^^^^ cannot move out of captured variable in an `Fn` closure
奇怪的是,如果我从参数列表中删除 error_msg 并简单地硬编码 Msg::TagsLoadError
它会编译,但请求不会 运行。 ♂️
ComponentLink::send_back()
期望 Fn
关闭。但是,您的闭包正在消耗一个捕获的变量,即 error_msg
,因此它只能被调用一次。这使得你的闭包实现 FnOnce
而不是 Fn
,所以它不能在那里使用。
更简单的查看方式是:
struct Foo;
fn call(f: impl Fn() -> Foo) {}
fn test(x: Foo) {
let cb = move || x;
call(cb);
}
完整的错误信息比较清楚:
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/lib.rs:6:14
|
6 | let cb = move || x;
| ^^^^^^^^-
| | |
| | closure is `FnOnce` because it moves the variable `x` out of its environment
| this closure implements `FnOnce`, not `Fn`
7 | call(cb);
| ---- the requirement to implement `Fn` derives from here
这是有道理的;如果你多次写 call(cb)
会发生什么?请记住,Foo
不可复制也不可克隆。
准确地说,最简单的解决方案是使您的类型可克隆,以便可以重复使用:
let cb = move || {
x.clone()
};
而且有效!
如果您不想要克隆的成本,您可以添加一些解决方法,例如传递一个 returns 错误的函数或某种引用计数指针。例如:
struct Foo;
fn call(f: impl Fn() -> Foo) {}
fn test(build_x: impl Fn() -> Foo) {
let cb = move || build_x();
call(cb);
}
这个能用是因为build_x
是一个Fn
,不是FnOnce
,所以用的时候不消耗,就是想调用多少次就调用多少次.
另一个没有回调的解决方法是使用 Option
并使用 Option::take
使用它。这会将其替换为 None
,并且从借用检查器的角度来看,该值继续存在。但是你需要一个 RefCell
因为否则你会改变一个捕获的变量并将你的闭包转换成一个 FnMut
.
use std::cell::RefCell;
struct Foo;
fn call(f: impl Fn() -> Foo) {}
fn test(x: Foo) {
let ox = RefCell::new(Some(x));
let cb = move || ox.borrow_mut().take().unwrap();
call(cb);
}
更新到最后一个选项
不要使用 RefCell
,而简单的 Cell
就可以。而 Cell
有一个 take
成员函数,使这段代码更简单:
use std::cell::Cell;
struct Foo;
fn call(f: impl Fn() -> Foo) {}
fn test(x: Foo) {
let ox = Cell::new(Some(x));
let cb = move || ox.take().unwrap();
call(cb);
}