davidcole1340/ext-php-rs

Lifetimes not supported with `php_class`

Closed this issue · 2 comments

This could be my lack of understanding as to what is possible via this crate and PHP; but lifetimes don't appear to be supported when used within struct attributes. E.g.

#[php_class]
pub struct MyBorrowedData<'a> {
    _data_origin_obj: ZendObject
    data: BinarySlice<'a, u8>
}

Causes this compiler error:

error[E0726]: implicit elided lifetime not allowed here
 --> foo-ext-php/src/lib.rs:7:12
  |
7 | pub struct MyBorrowedData<'a> {
  |            ^^^^^^^^^^^^^^^^ expected lifetime parameter
  |
  = note: assuming a `'static` lifetime...
help: indicate the anonymous lifetime
  |
7 | pub struct MyBorrowedData<'_><'a> {
  |                            ++++

error[E0726]: implicit elided lifetime not allowed here
  --> foo-ext-php/src/lib.rs:12:6
   |
12 | impl MyBorrowedData {
   |      ^^^^^^^^^^^^^^^^ expected lifetime parameter
   |
   = note: assuming a `'static` lifetime...
help: indicate the anonymous lifetime
   |
12 | impl MyBorrowedData<'_> {
   |                      ++++

error[E0726]: implicit elided lifetime not allowed here
  --> foo-ext-php/src/lib.rs:25:1
   |
25 | #[php_module]
   | ^^^^^^^^^^^^^ expected lifetime parameter
   |
   = note: assuming a `'static` lifetime...
   = note: this error originates in the attribute macro `php_module` (in Nightly builds, run with -Z macro-backtrace for more info)
help: indicate the anonymous lifetime
   |
25 | #[php_module]<'_>
   |              ++++

I'm trying to take some data from a PHP class object and store its data in a zero-copy fashion in a Rust struct, such that I may process it with as little overhead as possible. However, I'm aware that PHP may garbage collect the data associated with the borrows so I was hoping that taking a ZendObject of the origin PHP class object would prevent GC. I'm not entirely sure whether this matters, as the BinarySlice (or whatever PHP ext type give borrowed data) will hopefully mitigate this inherently I imagine.

Disclaimer

I'm only doing this because I don't think there's a way of calling object methods on arbitrary PHP objects (i.e. one whose class is not part of the the std library). Otherwise I'd just have a global function that takes an immutable reference to the object. Is that possible? If so this entire issue can be ignored :) I need the output of multiple object methods to be present at the same time, hence me trying to bring them all together in a PHP class that borrows data.

PS. awesome crate, it's thus far made my life infinitely easier.

WRT the restrictions for lifetimes and generics, I'm gonna grep the PyO3 documentation and s/Python/PHP/:

No lifetime parameters

Rust lifetimes are used by the Rust compiler to reason about a program's memory safety. They are a compile-time only concept; there is no way to access Rust lifetimes at runtime from a dynamic language like PHP.

As soon as Rust data is exposed to PHP, there is no guarantee which the Rust compiler can make on how long the data will live. PHP is a reference-counted language and those references can be held for an arbitrarily long time which is untraceable by the Rust compiler. The only possible way to express this correctly is to require that any #[php_class] does not borrow data for any lifetime shorter than the 'static lifetime, i.e. the #[php_class] cannot have any lifetime parameters.

When you need to share ownership of data between PHP and Rust, instead of using borrowed references with lifetimes consider using reference-counted smart pointers such as Arc.

No generic parameters

A Rust struct Foo<T> with a generic parameter T generates new compiled implementations each time it is used with a different concrete type for T. These new implementations are generated by the compiler at each usage site. This is incompatible with wrapping Foo in PHP, where there needs to be a single compiled implementation of Foo which is integrated with the PHP interpreter.

Makes sense. Thanks a lot. I can work around this.