I am writing a Rust application that uses the wgpu library to render stuff. How the library works is largely unimportant, since the errors I'm facing are lifetime-related.
In the function that actually performs the rendering looks like this: (You don't need to understand it, I listed it largely for completeness' sake.)
pub fn render(&self) -> anyhow::Result<()> {
let output = self.surface.get_current_texture()?;
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = self.device.create_command_encoder(
&wgpu::CommandEncoderDescriptor { label: Some("render encoder") }
);
let render_pass_descriptor = wgpu::RenderPassDescriptor {
label: Some("render pass"),
color_attachments: &[
wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(
wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0 }),
store: false,
}
},
],
depth_stencil_attachment: None,
};
let mut render_pass = encoder.begin_render_pass(&render_pass_descriptor);
render_pass.set_pipeline(&self.render_pipeline);
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
render_pass.draw(0..self.num_vertecies, 0..1);
// Explicitly drop, bc. it borrows the encoder.
drop(render_pass);
self.queue.submit(iter::once(encoder.finish()));
output.present();
Ok(())
}
I wanted to refactor this piece of code into a utility function, but keep the three calls on the render_pass object.
The utility function has this signature and does the same stuff the original function did, but instead of the three calls on render_pipeline, it just calls the render_pass_configurator closure:
pub fn submit_render_pass<F: FnOnce(wgpu::RenderPass)>(
surface: &wgpu::Surface,
device: &wgpu::Device,
queue: &wgpu::Queue,
clear_color: wgpu::Color,
render_pass_configurator: F,
) -> anyhow::Result<()> { ... }
And the body of the original render() function is replaced with the call to this utility function:
util::submit_render_pass(&self.surface, &self.device, &self.queue,
wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0 },
| mut render_pass | {
render_pass.set_pipeline(&self.render_pipeline);
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
render_pass.draw(0..self.num_vertecies, 0..1);
},
)
Seems straightforward to me, but of course Rust's borrow checker disagrees:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/gpu.rs:89:38
|
89 | render_pass.set_pipeline(&self.render_pipeline);
| ^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined here...
--> src/gpu.rs:85:19
|
85 | pub fn render(&self) -> anyhow::Result<()> {
| ^^^^^
note: ...so that reference does not outlive borrowed content
--> src/gpu.rs:89:38
|
89 | render_pass.set_pipeline(&self.render_pipeline);
| ^^^^^^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the anonymous lifetime #1 defined here...
--> src/gpu.rs:88:9
|
88 | / | mut render_pass: wgpu::RenderPass | {
89 | | render_pass.set_pipeline(&self.render_pipeline);
90 | | render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
91 | | render_pass.draw(0..self.num_vertecies, 0..1);
92 | | },
| |_________^
note: ...so that the types are compatible
--> src/gpu.rs:89:25
|
89 | render_pass.set_pipeline(&self.render_pipeline);
| ^^^^^^^^^^^^
= note: expected `&mut RenderPass<'_>`
found `&mut RenderPass<'_>`
(... and a similar error for the .slice(..) call.)
I understand that because of the render_pass.set_pipeline(&self.render_pipeline) call, render_pass may not ever live longer than &self. But it doesn't. render_pipeline gets dropped at the end of the closure, and &self lives on.
I tried adding lifetimes to the best of my ability, and I got the error to change only once when I added an explicit lifetime to the type of the utility function, changing the closure definition to F: FnOnce(wgpu::RenderPass<'a>): (But this error didn't make much sense to me either.)
error[E0597]: `view` does not live long enough
--> src/gpu_util.rs:130:23
|
112 | pub fn submit_render_pass<'a, F: FnOnce(wgpu::RenderPass<'a>)>(
| -- lifetime `'a` defined here
...
130 | view: &view,
| ^^^^^ borrowed value does not live long enough
...
143 | render_pass_configurator(render_pass);
| ------------------------------------- argument requires that `view` is borrowed for `'a`
...
149 | }
| - `view` dropped here while still borrowed
Update
I got it to work by writing the render() function like this: (Self == GpuState)
pub fn render(&self) -> anyhow::Result<()> {
fn configure_render_pass<'a>(s: &'a GpuState, mut render_pass: wgpu::RenderPass<'a>) {
render_pass.set_pipeline(&s.render_pipeline);
render_pass.set_vertex_buffer(0, s.vertex_buffer.slice(..));
render_pass.draw(0..s.num_vertecies, 0..1);
}
util::submit_render_pass(&self.surface, &self.device, &self.queue,
wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0 },
| render_pass: wgpu::RenderPass<'_> | {
configure_render_pass(self, render_pass);
},
)
}
I think what makes it work here is that I get a chance to explicitly tell the compiler that the captured self lives as long as the render_pass. But that's only my guess...
I'll leave the question open, if anyone comes up with a solution to make it work without the extra function declaration.
pub fn submit_render_pass<F: FnOnce(wgpu::RenderPass)>(
surface: &wgpu::Surface,
device: &wgpu::Device,
queue: &wgpu::Queue,
clear_color: wgpu::Color,
render_pass_configurator: F,
) -> anyhow::Result<()> { ... }
First of, all, you have a hidden lifetime parameter, which can give rise to very confusing errors. Add the #![deny(elided_lifetimes_in_paths)] lint setting to your code to force these to be explicit. (It's unfortunate that that lint isn't on by default.) You'll be required to change the code to…
pub fn submit_render_pass<F: FnOnce(wgpu::RenderPass<'???>)>(
And now we see part of the problem: what goes in the spot I've marked '???? The RenderPass borrows from the CommandEncoder (the signature of CommandEncoder::begin_render_pass() tells us that), but the CommandEncoder is a local variable inside submit_render_pass(), so borrows of it cannot have a lifetime that is nameable from outside the function.
To solve this problem, you need to use a HRTB to specify that the callback function must be able to accept any lifetime:
pub fn submit_render_pass<F>(
surface: &wgpu::Surface,
device: &wgpu::Device,
queue: &wgpu::Queue,
clear_color: wgpu::Color,
render_pass_configurator: F,
) -> anyhow::Result<()>
where
F: for<'encoder> FnOnce(wgpu::RenderPass<'encoder>)
{ ... }
Related
I have a newtype struct:
pub struct Branch<'repo>(Git2Branch<'repo>);
And I am trying to wrap a method call:
impl <'repo> Branch<'_> {
pub fn into_reference(self) -> Git2Reference<'repo> {
self.0.into_reference()
}
}
(Git2Branch and Git2Reference here are aliases for types of the same name from the git2 crate.)
This fails to compile with
error: lifetime may not live long enough
--> git_wrapper/src/branch.rs:38:9
|
6 | impl <'repo> Branch<'_> {
| ----- lifetime `'repo` defined here
...
37 | pub fn into_reference(self) -> Git2Reference<'repo> {
| ---- has type `branch::Branch<'1>`
38 | self.0.into_reference()
| ^^^^^^^^^^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'repo` but it is returning data with lifetime `'1`
Lifetimes '1 and 'repo should be the same, but I can't figure out how (or if it's even possible) to specify the lifetime of self in this case.
impl <'repo> Branch<'_> {
Is the same as:
impl <'repo, 'anonymous> Branch<'anonymous> {
So the lifetimes 'repo and the anonymous lifetime of the field are unrelated - which causes an issue when you try to return the Git2Reference<'anonymous>.
You only need to talk about one lifetime here, so say that:
impl <'repo> Branch<'repo> {
As you can see in the following code, I have two traits, one is called Hittable, and the other is called Material (I have been studying the book "ray-tracing-in-one-weekend", but use Rust).
The Hittable trait implements hit function for some objects (just like Sphere in this code), and every kind of objects includes its material (just like Glass, Wood...).
In my real project, the Sphere struct and another struct (called HitRecord in this book, used as mut reference to pass result in hit function), they both include &dyn Material, so that I need add lifetime parameter for both of them. However, to accomplish that, I should add lifetime parameter in the trait declaration, so I can assign the same lifetime parameter for Sphere and hit.
But the compiler indicates that the reference still under borrowed when the main function ends, I have no idea for that...
trait Hittable<'a> {
fn hit(&self);
}
trait Material {
fn result(&self);
}
struct Glass;
impl Material for Glass {
fn result(&self) {
println!("Glass is broken!");
}
}
struct Sphere<'a> {
name: String,
mat_ptr: &'a dyn Material,
}
impl<'a> Hittable<'a> for Sphere<'a> {
fn hit(&self) {
println!("Name is {}", self.name);
self.mat_ptr.result();
}
}
struct HT<'a> {
pub objects: Vec<Box<dyn Hittable<'a>>>,
}
fn main() {
let mut list = HT { objects: vec![] };
let surface_material = Glass;
let s = Sphere {
name: String::from("球"),
mat_ptr: &surface_material,
};
list.objects.push(Box::new(s));
}
the message shows
Compiling rust_test v0.1.0 (/home/hnyls2002/rust_test)
error[E0597]: `surface_material` does not live long enough
--> src/main.rs:38:18
|
38 | mat_ptr: &surface_material,
| ^^^^^^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| cast requires that `surface_material` is borrowed for `'static`
...
41 | }
| - `surface_material` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
This is because dyn Hittable<'a> is actually dyn Hittable<'a> + 'static, and thus s is required to live for 'static. The fix is to change HT to:
struct HT<'a> {
pub objects: Vec<Box<dyn Hittable<'a> + 'a>>,
}
Then you'll get a long but pretty self-explanatory error:
error[E0597]: `surface_material` does not live long enough
--> src/main.rs:38:18
|
38 | mat_ptr: &surface_material,
| ^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
41 | }
| -
| |
| `surface_material` dropped here while still borrowed
| borrow might be used here, when `list` is dropped and runs the destructor for type `HT<'_>`
|
= note: values in a scope are dropped in the opposite order they are defined
Because surface_material is defined after list, it'll be dropped before and when the potential destructor for dyn Hittable will run it may access the freed surface_material. The fix is just to swap the declaration order of list and surface_material:
fn main() {
// always define before the container you are pushing into
let surface_material = Glass;
let mut list = HT { objects: vec![] };
// ...
}
Let thread state consists of immutable parameters Params and the rest of the (mutable) state State.
I am trying to mock spawning a thread that does something being controlled by parameters Params:
use std::thread;
struct Params {
x: i32,
}
struct State<'a> {
params: &'a Params,
y: i32,
}
impl<'a> State<'a> {
fn new(params: &Params) -> State {
State {
params,
y: 0,
}
}
fn start(&mut self) -> thread::JoinHandle<()> {
let params = self.params.clone();
thread::spawn(move || { params; /* ... */ })
}
}
But this does not work:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> test.rs:20:34
|
20 | let params = self.params.clone();
| ^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 12:6...
--> test.rs:12:6
|
12 | impl<'a> State<'a> {
| ^^
note: ...so that the types are compatible
--> test.rs:20:34
|
20 | let params = self.params.clone();
| ^^^^^
= note: expected `&&Params`
found `&&'a Params`
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure#test.rs:21:23: 21:42 params:&Params]` will meet its required lifetime bounds
--> test.rs:21:9
|
21 | thread::spawn(move || { params; /* ... */ })
| ^^^^^^^^^^^^^
I understand why it does not work: The thread could run indefinitely long, and the params could be destroyed before it is terminated. That's clearly an error.
Now explain what is the proper way to make params long at least as long as the thread. In other words, help to correct the above code. What should I do with lifetimes?
You got the right idea with using clone and a move lambda, but you forgot one detail: Params isn't Clone! Therefore the compiler did the best it could when it saw self.params.clone() and cloned… the reference.
That's why the error messages have two & here:
= note: expected `&&Params`
found `&&'a Params`
Your issue is solved by using #[derive(Clone)] struct Params { /* … */ }.
I have the following rust code cannot be compiled.
struct Person {
name : String,
age : u8,
}
fn main() {
let p = Person{ name: "Nobody".to_string(), age : 24};
let age = |p : &Person| p.age;
let name = |p : &Person | &p.name;
println! ("name={}, age={}" , name(&p), age(&p));
}
And the compiler gave the following error message.
Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/main.rs:11:31
|
11 | let name = |p : &Person | &p.name;
| ^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 11:16...
--> src/main.rs:11:16
|
11 | let name = |p : &Person | &p.name;
| ^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:11:31
|
11 | let name = |p : &Person | &p.name;
| ^^^^^^^
note: but, the lifetime must be valid for the expression at 2:29...
--> src/main.rs:13:5
|
13 | println! ("name={}, age={}" , name(&p), age(&p));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so type `(&&std::string::String, &u8)` of expression is valid during the expression
--> src/main.rs:13:5
|
13 | println! ("name={}, age={}" , name(&p), age(&p));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error: aborting due to previous error
I tried to add the lifetime for name closure.
let name<'a> = |p : &'a Person | -> &'a String { &'a p.name };
but still got the compiler error
Compiling playground v0.0.1 (/playground)
error: expected one of `:`, `;`, `=`, `#`, or `|`, found `<`
--> src/main.rs:12:13
|
12 | let name<'a> = |p : &'a Person | -> &'a String { &'a p.name };
| ^ expected one of `:`, `;`, `=`, `#`, or `|`
error: aborting due to previous error
Just want to know how to write the correct code.
One other solution is to give an explicit type to your closure. Unfortunately, you can't use its actual type, but you can cast it to a function pointer.
Remember that the issue is that the compiler isn't able to correctly deduce that the lifetime of the output is tied to the lifetime of the input (it could be an instance of this bug, but I'm not at all sure). We can fix that by making the lifetimes explicit.
struct Person {
name: String,
age: u8,
}
fn main() {
let p = Person {
name: "Nobody".to_string(),
age: 24,
};
let age = |p: &Person| p.age;
// Our only changes are right here.
let name: for<'a> fn(&'a Person) -> &'a String = |p: &Person| &p.name;
println!("name={}, age={}", name(&p), age(&p));
}
(playground)
In fact, it's possible to be slightly less explicit than this. The compiler is fine figuring out the types of the input and output. It's just the lifetimes it has trouble with. So replacing that line with let name: for<'a> fn(&'a _) -> &'a _ = |p: &Person| &p.name; also works (playground).
For me it is more important to understand the source of the problem that find a workaround,
so step by step. let's start from something that works:
struct Person {
name: String,
age: u8,
}
fn get_name<'a>(person: &'a Person) -> &'a str {
&person.name
}
fn main() {
let p = Person {
name: "Nobody".to_string(),
age: 24,
};
let age = |p: &Person| p.age;
let name = get_name;
println!("name={}, age={}", name(&p), age(&p));
}
There is no issues when using a function instead of a clousure. In this case,
the compiler is able to check that lifetime requirements are ok.
But when trying to use a closure for name:
let name = |p : &Person | &p.name;
You get the cannot infer an appropriate lifetime error.
Why?
A closure captures its environment: some opaque struct has to be created
by the compiler and such struct has to be callable.
I'm non fully aware of the internal details, but something along these lines would be created when desugaring your closure:
struct OpaqueType<'a> {
// a PhantomData because you don't capure nothing
// just to make explicit that struct lifetime bind to environment
// if you would had captured some integer:
// captured_int: &'a i32,
captured_int: PhantomData<&'a i32>,
}
impl<'a> OpaqueType<'a> {
fn call<'b>(&'b self, person: &'a Person) -> &'a str {
&person.name
}
}
And looking at call it is apparent that when a clusure argument is a reference there are two unrelated lifetimes at play.
Finally the answer: how to return a reference
Also note that in your case, not declaring the argument type and using the helper function get_name, works:
// let name = |p| &p.name; // does not work, not enough info to infer p type
let name = |p| get_name(p);
My guess is that in such case the compiler, following some inference path, is able to desugar in a way that lifetimes are bounded as expected.
How do I pass an instance of EcsClient with the signature impl<P, D> EcsClient<P, D> where P: ProvideAwsCredentials, D: DispatchSignedRequest to a function as a reference in Rust? My attempt is thus:
extern crate rusoto;
use std::default::Default;
use rusoto::{ DefaultCredentialsProvider, Region };
use rusoto::ecs::{ EcsClient };
use rusoto::default_tls_client;
fn get_task_definition_revisions(client: &EcsClient) {
// Use EscClient instance here
}
fn main() {
let provider = DefaultCredentialsProvider::new().unwrap();
let client = EcsClient::new(default_tls_client().unwrap(), provider, Region::EuWest1).unwrap();
get_task_definition_revisions(&client);
}
This gives me the following error:
error[E0243]: wrong number of type arguments: expected 2, found 0
--> src/main.rs:9:43
|
9 | fn get_task_definition_revisions(client: &EcsClient) {
| ^^^^^^^^^ expected 2 type arguments
My attempted fix for this is such:
extern crate rusoto;
use std::default::Default;
use rusoto::{
DefaultCredentialsProvider,
Region,
ProvideAwsCredentials,
DispatchSignedRequest
};
use rusoto::ecs::{ EcsClient, ListTaskDefinitionsRequest };
use rusoto::default_tls_client;
fn get_task_definition_revisions(client: &EcsClient<ProvideAwsCredentials, DispatchSignedRequest>) {
// Use EcsClient instance here
}
fn main() {
let provider = DefaultCredentialsProvider::new().unwrap();
let client = EcsClient::new(default_tls_client().unwrap(), provider, Region::EuWest1);
get_task_definition_revisions(&client);
}
Which gives me:
error[E0277]: the trait bound `rusoto::ProvideAwsCredentials + 'static: std::marker::Sized` is not satisfied
--> src/main.rs:14:1
|
14 | fn get_task_definition_revisions(client: &EcsClient<P, D>) {
| _^ starting here...
15 | | let defs = client.list_task_definitions(&ListTaskDefinitionsRequest {
16 | | family_prefix: None,
17 | | max_results: None,
18 | | next_token: None,
19 | | sort: None,
20 | | status: Some("ACTIVE".to_string()),
21 | | });
22 | | }
| |_^ ...ending here: the trait `std::marker::Sized` is not implemented for `rusoto::ProvideAwsCredentials + 'static`
|
= note: `rusoto::ProvideAwsCredentials + 'static` does not have a constant size known at compile-time
= note: required by `rusoto::ecs::EcsClient`
error[E0277]: the trait bound `rusoto::DispatchSignedRequest + 'static: std::marker::Sized` is not satisfied
--> src/main.rs:14:1
|
14 | fn get_task_definition_revisions(client: &EcsClient<P, D>) {
| _^ starting here...
15 | | let defs = client.list_task_definitions(&ListTaskDefinitionsRequest {
16 | | family_prefix: None,
17 | | max_results: None,
18 | | next_token: None,
19 | | sort: None,
20 | | status: Some("ACTIVE".to_string()),
21 | | });
22 | | }
| |_^ ...ending here: the trait `std::marker::Sized` is not implemented for `rusoto::DispatchSignedRequest + 'static`
|
= note: `rusoto::DispatchSignedRequest + 'static` does not have a constant size known at compile-time
= note: required by `rusoto::ecs::EcsClient`
This feels like a rabbit hole I shouldn't be going down.
I've also tried changing the function signature to accept generics, however the EcsClient is a struct not a trait. Googling doesn't provide much help because I don't know the correct terms to search for.
This question seems to imply that I should be able to declare a function like fn my_func(client: &EcsClient) { ... } and it will work, so why doesn't the above example?
The problem is that EcsClient is not a type, it's a blueprint to build a type (also known as "type constructor").
As a result, you cannot use just EcsClient when a type is required, be it in functions or for struct members; instead, each time, you must use it to build a type by specifying its generic parameters.
Thus, the first step is to introduce the type parameters:
fn get_task_definition_revisions<P, D>(client: &EcsClient<P, D>) {}
Now, however, the compiler will complain that the P and D are insufficiently constrained: EcsClient only accept a very specific kind of P and D!
The next step, thus, is to look-up the bounds that are specified for P and D in the definition of EcsClient and apply them. It's just copy/paste at this point:
fn get_task_definition_revisions<P, D>(client: &EcsClient<P, D>)
where P: ProvideAwsCredentials,
D: DispatchSignedRequest
{
}
And then you're golden.
If you need more capabilities of P or D for this specific function, feel free to constrain them adequately by adding more bounds using +:
fn get_task_definition_revisions<P, D>(client: &EcsClient<P, D>)
where P: ProvideAwsCredentials + 'static,
D: DispatchSignedRequest
{
}
If you wonder why Rust chose to have you repeat the bounds for P and D when it could perfectly infer them, it's because it cares about you. More specifically, it cares about you 6 months from now, and the next maintainer to come. So, taking the stance that you write once and read many, it forces you to copy the bounds so that later you don't have to wonder what they are, and drill down recursively in each type/function used to painfully aggregate all the pieces. In Rust, the next time you read the function, you'll have all the information right there.
I was understanding the syntax incorrectly. It seems that P and D are placeholders, like T would be. I need to specify what those types are, so the signature now looks like this:
fn get_task_definition_revisions<P: ProvideAwsCredentials, D: DispatchSignedRequest>(client: &EcsClient<P, D>) {
...
}
I don't use P or D in the function body, but they must be declared.
The full example now looks like:
extern crate rusoto;
use std::default::Default;
use rusoto::{
DefaultCredentialsProvider,
Region,
ProvideAwsCredentials,
DispatchSignedRequest
};
use rusoto::ecs::{ StringList, EcsClient, ListTaskDefinitionsRequest };
use rusoto::default_tls_client;
fn get_task_definition_revisions<P: ProvideAwsCredentials, D: DispatchSignedRequest>(client: &EcsClient<P, D>) {
let res = client.list_task_definitions(&ListTaskDefinitionsRequest {
family_prefix: None,
max_results: None,
next_token: None,
sort: None,
status: Some("ACTIVE".to_string()),
});
// ...
}
fn main() {
let provider = DefaultCredentialsProvider::new().unwrap();
let client = EcsClient::new(default_tls_client().unwrap(), provider, Region::EuWest1);
get_task_definition_revisions(&client);
}
I'm still not entirely sure why this works or is required but I hope this answer helps someone else.