"cannot infer an appropriate lifetime" 尝试 return 使用 hyper 的分块响应时
"cannot infer an appropriate lifetime" when attempting to return a chunked response with hyper
我想要 return 特定大小块中的二进制数据。这是一个minimal example.
我为 hyper::Response
制作了一个包装器结构来保存我的数据,如状态、状态文本、headers 和 return 的资源:
pub struct Response<'a> {
pub resource: Option<&'a Resource>
}
这个结构有一个 build
方法创建 hyper::Response
:
impl<'a> Response<'a> {
pub fn build(&mut self) -> Result<hyper::Response<hyper::Body>, hyper::http::Error> {
let mut response = hyper::Response::builder();
match self.resource {
Some(r) => {
let chunks = r.data
.chunks(100)
.map(Result::<_, std::convert::Infallible>::Ok);
response.body(hyper::Body::wrap_stream(stream::iter(chunks)))
},
None => response.body(hyper::Body::from("")),
}
}
}
还有另一个结构保存数据库内容:
pub struct Resource {
pub data: Vec<u8>
}
一切正常,直到我尝试创建分块响应。 Rust 编译器给我以下错误:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:14:15
|
14 | match self.resource {
| ^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 11:6...
--> src/main.rs:11:6
|
11 | impl<'a> Response<'a> {
| ^^
note: ...so that the types are compatible
--> src/main.rs:14:15
|
14 | match self.resource {
| ^^^^^^^^^^^^^
= note: expected `Option<&Resource>`
found `Option<&'a Resource>`
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the types are compatible
--> src/main.rs:19:31
|
19 | response.body(hyper::Body::wrap_stream(stream::iter(chunks)))
| ^^^^^^^^^^^^^^^^^^^^^^^^
= note: expected `From<&[u8]>`
found `From<&'static [u8]>`
我不知道如何满足这些终身要求。我怎样才能正确地做到这一点?
问题不在于'a
本身,而在于std::slice::chunks()
函数returns是一个借用原始切片的迭代器。您正在尝试从这个 Chunks<'_, u8>
值创建流未来,但流要求它是 'static
。即使你的 Resource
没有 'a
生命周期,你仍然会借用 r.data
,它仍然会失败。
请记住,这里 'static
并不意味着该值永远存在,而是可以使其在需要时一直存在。也就是说,未来不得持有任何(非'static
)次借贷。
您可以克隆所有数据,但如果数据非常大,成本可能会很高。如果是这样,您可以尝试使用 Bytes
,这就像 Vec<u8>
但引用计数。
看起来没有 Bytes::chunks()
函数 returns 是 Bytes
的迭代器。幸运的是,手工操作很容易。
最后,请记住 Rust 中的迭代器是惰性的,因此它们会保留借用的原始数据,即使它是 Bytes
。所以我们需要将它们收集到一个 Vec
中才能真正拥有数据 (playground):
pub struct Resource {
pub data: Bytes,
}
impl<'a> Response<'a> {
pub fn build(&mut self) -> Result<hyper::Response<hyper::Body>, hyper::http::Error> {
let mut response = hyper::Response::builder();
match self.resource {
Some(r) => {
let len = r.data.len();
let chunks = (0..len)
.step_by(100)
.map(|x| {
let range = x..len.min(x + 100);
Ok(r.data.slice(range))
})
.collect::<Vec<Result<Bytes, std::convert::Infallible>>>();
response.body(hyper::Body::wrap_stream(stream::iter(chunks)))
}
None => response.body(hyper::Body::from("")),
}
}
}
更新:如果我们注意到 stream::iter()
拥有可以延迟计算的 IntoIterator
的所有权,我们可以避免调用 collect()
,只要我们做到 'static
。如果我们对 r.data
进行(廉价)克隆并将其移至 lambda (playground):
中,就可以做到这一点
let data = r.data.clone();
let len = data.len();
let chunks = (0..len).step_by(100)
.map(move |x| {
let range = x .. len.min(x + 100);
Result::<_, std::convert::Infallible>::Ok(data.slice(range))
});
response.body(hyper::Body::wrap_stream(stream::iter(chunks)))
我想要 return 特定大小块中的二进制数据。这是一个minimal example.
我为 hyper::Response
制作了一个包装器结构来保存我的数据,如状态、状态文本、headers 和 return 的资源:
pub struct Response<'a> {
pub resource: Option<&'a Resource>
}
这个结构有一个 build
方法创建 hyper::Response
:
impl<'a> Response<'a> {
pub fn build(&mut self) -> Result<hyper::Response<hyper::Body>, hyper::http::Error> {
let mut response = hyper::Response::builder();
match self.resource {
Some(r) => {
let chunks = r.data
.chunks(100)
.map(Result::<_, std::convert::Infallible>::Ok);
response.body(hyper::Body::wrap_stream(stream::iter(chunks)))
},
None => response.body(hyper::Body::from("")),
}
}
}
还有另一个结构保存数据库内容:
pub struct Resource {
pub data: Vec<u8>
}
一切正常,直到我尝试创建分块响应。 Rust 编译器给我以下错误:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:14:15
|
14 | match self.resource {
| ^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 11:6...
--> src/main.rs:11:6
|
11 | impl<'a> Response<'a> {
| ^^
note: ...so that the types are compatible
--> src/main.rs:14:15
|
14 | match self.resource {
| ^^^^^^^^^^^^^
= note: expected `Option<&Resource>`
found `Option<&'a Resource>`
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the types are compatible
--> src/main.rs:19:31
|
19 | response.body(hyper::Body::wrap_stream(stream::iter(chunks)))
| ^^^^^^^^^^^^^^^^^^^^^^^^
= note: expected `From<&[u8]>`
found `From<&'static [u8]>`
我不知道如何满足这些终身要求。我怎样才能正确地做到这一点?
问题不在于'a
本身,而在于std::slice::chunks()
函数returns是一个借用原始切片的迭代器。您正在尝试从这个 Chunks<'_, u8>
值创建流未来,但流要求它是 'static
。即使你的 Resource
没有 'a
生命周期,你仍然会借用 r.data
,它仍然会失败。
请记住,这里 'static
并不意味着该值永远存在,而是可以使其在需要时一直存在。也就是说,未来不得持有任何(非'static
)次借贷。
您可以克隆所有数据,但如果数据非常大,成本可能会很高。如果是这样,您可以尝试使用 Bytes
,这就像 Vec<u8>
但引用计数。
看起来没有 Bytes::chunks()
函数 returns 是 Bytes
的迭代器。幸运的是,手工操作很容易。
最后,请记住 Rust 中的迭代器是惰性的,因此它们会保留借用的原始数据,即使它是 Bytes
。所以我们需要将它们收集到一个 Vec
中才能真正拥有数据 (playground):
pub struct Resource {
pub data: Bytes,
}
impl<'a> Response<'a> {
pub fn build(&mut self) -> Result<hyper::Response<hyper::Body>, hyper::http::Error> {
let mut response = hyper::Response::builder();
match self.resource {
Some(r) => {
let len = r.data.len();
let chunks = (0..len)
.step_by(100)
.map(|x| {
let range = x..len.min(x + 100);
Ok(r.data.slice(range))
})
.collect::<Vec<Result<Bytes, std::convert::Infallible>>>();
response.body(hyper::Body::wrap_stream(stream::iter(chunks)))
}
None => response.body(hyper::Body::from("")),
}
}
}
更新:如果我们注意到 stream::iter()
拥有可以延迟计算的 IntoIterator
的所有权,我们可以避免调用 collect()
,只要我们做到 'static
。如果我们对 r.data
进行(廉价)克隆并将其移至 lambda (playground):
let data = r.data.clone();
let len = data.len();
let chunks = (0..len).step_by(100)
.map(move |x| {
let range = x .. len.min(x + 100);
Result::<_, std::convert::Infallible>::Ok(data.slice(range))
});
response.body(hyper::Body::wrap_stream(stream::iter(chunks)))