-
Notifications
You must be signed in to change notification settings - Fork 763
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: allow async methods to accept &self
/&mut self
#3609
Conversation
c267372
to
93379bc
Compare
&self
/&mut self
CodSpeed Performance ReportMerging #3609 will not alter performanceComparing Summary
|
2752929
to
706c327
Compare
45f1cfd
to
77421ec
Compare
As previously mentioned in #3540 (comment), there is a design decision to be discussed here.
In this PR, I've implemented the first one, as illustrated in the related test, because I think it's the simpler to reason about. Another solution could be to expose |
It seems my current implementation has a big MSRV issue, because it only works in 1.74 🤔 I don't find anything in 1.74 changelog that would explain the error observed in the CI job. I will investigate more. |
@wyfo I got a bit lost with the recent opening of several PRs, this is the only one not in draft, are you waiting for reviews on any of these (or this one)? |
Indeed, sorry, I should have made it more explicit.
That's what I've done, but with PR based on a previous one as draft, because they can't be merged before their base PR. |
8089e34
to
3f15d2a
Compare
Ok, now it seems to work in 1.56, so it's ready to review/merge. |
d94c9e7
to
6d3129e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! Review posted, sorry for being generally slow :)
@@ -30,8 +30,7 @@ Resulting future of an `async fn` decorated by `#[pyfunction]` must be `Send + ' | |||
|
|||
As a consequence, `async fn` parameters and return types must also be `Send + 'static`, so it is not possible to have a signature like `async fn does_not_compile(arg: &PyAny, py: Python<'_>) -> &PyAny`. | |||
|
|||
It also means that methods cannot use `&self`/`&mut self`, *but this restriction should be dropped in the future.* | |||
|
|||
However, there is an exception for method receiver, so async methods can accept `&self`/`&mut self` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth pointing out here that there's a good case for being extra careful with &mut self
as a method receiver in a future? I think it's very likely users will accidentally hit borrow errors due to overlapping futures.
We could potentially restrict this to &self
on frozen classes? That would help prevent users creating aliasing borrow errors from async code. What do you think about that restriction?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could potentially restrict this to &self on frozen classes?
Honestly, I prefer to just add a warning in the documentation. I don't want to prevent careful users to use a mpsc::Receiver
in an efficient way.
Maybe you could ping another maintainer to discuss this decision?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok let's keep this. I think what I'd prefer to do is make frozen the default and then document what the risks of &mut
are if users choose to enable this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But that is a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me, thanks 👍
I am obviously late to the party, but I think there is a reason besides simplicity for choosing the first option: I think it also matches the behaviour of plain Rust async methods, i.e. writing In any case, if I had fast enough to join the fray, I would have asked for the guide entry to expand on this, i.e. to explicitly call out how long the method receiver borrow lasts and that this is natural transfer of the existing semantics of async methods. |
@wyfo Thanks for your great work. May I have a question?
#[pymethods]
impl Counter {
#[new]
fn new() -> Self {
Self(0)
}
async fn get(&self) -> usize {
self.0
}
async fn incr(&mut self) -> usize {
self.0 += 1;
self.0
}
// Error occurs here because of `num`
async fn incr_num(&mut self, num: usize) -> Result<usize, Error> {
self.0 += num;
Ok(self.0)
}
} |
@jopemachine let's split that out into a new discussion. Probably the "argument extraction" code for |
Relates to #1632
Draft based on #3608