PyO3 / pyo3

Rust bindings for the Python interpreter
https://pyo3.rs
Apache License 2.0
12.41k stars 766 forks source link

`#[derive(IntoPyObject)]` error a bit unhelpful when lifetime not called `'py` #4704

Open davidhewitt opened 1 week ago

davidhewitt commented 1 week ago

In pydantic-core some (legacy) lifetimes were 'a instead of 'py, and using the new #[derive(IntoPyObject)] led to some interesting errors:

use pyo3::{Bound, IntoPyObject, PyAny};

#[derive(IntoPyObject)]
pub struct Wrap<'a>(Bound<'a, PyAny>);

gets lots of errors like

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'py` due to conflicting requirements
 --> src/lib.rs:3:10
  |
3 | #[derive(IntoPyObject)]
  |          ^^^^^^^^^^^^
  |
note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
 --> src/lib.rs:4:17
  |
4 | pub struct Wrap<'a>(Bound<'a, PyAny>);
  |                 ^^
note: ...but the lifetime must also be valid for the lifetime `'py` as defined here...
 --> src/lib.rs:3:10
  |
3 | #[derive(IntoPyObject)]
  |          ^^^^^^^^^^^^
note: ...so that the types are compatible
 --> src/lib.rs:3:10
  |
3 | #[derive(IntoPyObject)]
  |          ^^^^^^^^^^^^
  = note: expected `pyo3::IntoPyObject<'py>`
             found `pyo3::IntoPyObject<'_>`
  = note: this error originates in the derive macro `IntoPyObject` (in Nightly builds, run with -Z macro-backtrace for more info)

changing the struct lifetime from 'a to 'py trivially resolves, though I wonder if there's any tricks we can do to improve the UX 🤔

alex commented 1 week ago

I haven't looked at the impl, but FWIW what I do in rust-asn1 is to look if the struct already has a lifetime, and if so use that lifetime.

Icxolu commented 1 week ago

The reason for the special case is that we do support lifetimes other than 'py, eg this works:

#[derive(IntoPyObject)]
struct Foo<'a>(&'a str);

But I can have a look if there is a way to improve the error message.

davidhewitt commented 1 week ago

Yes, I thought it was something like that special case where the lifetime is unrelated 👍

One thought I had is that maybe with GATs the lifetime would be unnecessary on the trait and could go on the Output associated type. Covariance might allow things to just work. That's a future possibility though rather than something we can use now.