Bindings to C library with intrusive linked lists

Hi!

I'm playing with Rust and I'm trying to write prototype Rust bindings for wlroots, a Wayland compositor library.

wlroots makes extensive use of intrusive linked lists (they are similar to the kernel's). That means I need to use Pin to ensure the pointers aren't moved around by Rust.

My current WIP code is here: ~emersion/wlroots-rs: wlroots-sys/examples/basic.rs - sourcehut git

I'm currently trying to generalize the logic a bit in this branch: ~emersion/wlroots-rs: wlroots-sys/examples/basic.rs - sourcehut git

However I'm hitting this error:

error[E0401]: can't use generic parameters from outer function
   --> examples/basic.rs:137:21
    |
126 | impl<D> Backend<D> where D: BackendHandler {
    |      - type parameter from outer function
127 |     pub fn autocreate(display: &Display<D>) -> Result<Pin<Box<Backend<D>>>, ()> {
    |            ---------- try adding a local generic parameter in this method instead
...
137 |             Backend<D>,
    |                     ^ use of generic parameter from outer function

Any ideas how to fix that? I can't wrap my head around this error. Maybe I need to change how my offset_of! macro works?

Simon

The handle fn would need to be generic itself for this to work.

I note that you don't necessarily need Pin. Storing a raw pointer to the allocation will work just as well as a Pin<Box<...>>, and may fit better together with the C API you're trying to talk to.

1 Like

Hm, I've tried doing this, but it doesn't work…

diff --git a/wlroots-sys/examples/basic.rs b/wlroots-sys/examples/basic.rs
index 6b691fba2b86..ee85789721b4 100644
--- a/wlroots-sys/examples/basic.rs
+++ b/wlroots-sys/examples/basic.rs
@@ -78,9 +78,13 @@ impl Listener {
 
 macro_rules! init_listener {
     ($ptr:expr, $container:ty, $field:ident, $signal:expr, $handler:ident) => {{
-        extern "C" fn handle(l: *mut wl_listener, _: *mut c_void) {
+        trait Handler {
+            fn $handler(&mut self);
+        }
+
+        extern "C" fn handle<T>(l: *mut wl_listener, _: *mut c_void) where T: Handler {
             let ptr = unsafe {
-                let ptr = container_of!(l, $container, $field.listener) as *mut $container;
+                let ptr = container_of!(l, T, $field.listener) as *mut T;
                 ptr.as_mut().unwrap()
             };
             ptr.$handler();
@@ -90,7 +94,7 @@ macro_rules! init_listener {
             let l = $ptr
                 .as_mut()
                 .map_unchecked_mut(|container| &mut container.$field);
-            l.init($signal, handle);
+            l.init($signal, handle::<$container>);
         }
     }};
 }
error[E0277]: the trait bound `Backend<D>: Handler` is not satisfied
   --> examples/basic.rs:97:29
    |
97  |               l.init($signal, handle::<$container>);
    |                               ^^^^^^^^^^^^^^^^^^^^ the trait `Handler` is not implemented for `Backend<D>`
...
139 | /         init_listener!(
140 | |             backend,
141 | |             Backend<D>,
142 | |             destroy,
143 | |             &mut ptr.events.destroy,
144 | |             handle_destroy
145 | |         );
    | |_________- in this macro invocation
    |
note: required by a bound in `handle`
   --> examples/basic.rs:85:79
    |
85  |           extern "C" fn handle<T>(l: *mut wl_listener, _: *mut c_void) where T: Handler {
    |                                                                                 ^^^^^^^ required by this bound in `handle`
...
139 | /         init_listener!(
140 | |             backend,
141 | |             Backend<D>,
142 | |             destroy,
143 | |             &mut ptr.events.destroy,
144 | |             handle_destroy
145 | |         );
    | |_________- in this macro invocation
    = note: this error originates in the macro `init_listener` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0609]: no field `destroy` on type `T`
   --> examples/basic.rs:142:13
    |
85  |         extern "C" fn handle<T>(l: *mut wl_listener, _: *mut c_void) where T: Handler {
    |                              - type parameter 'T' declared here
...
142 |             destroy,
    |             ^^^^^^^

It definitely did work! Your new error is completely unrelated, and the error you asked about was solved.

1 Like

… This solution replaces an error with another error. I am not sure it's the correct way forward. Any idea how to fix it?

I don't know why you get the error you get, since I can't see the full code. Maybe you can post a playground with the full code?

The full code is in the Git repo. Branch handler, as linked above.

I think I've managed to get to the next step (now hitting yet another error with RefCell), but it's not looking pretty at all.

diff --git a/wlroots-sys/examples/basic.rs b/wlroots-sys/examples/basic.rs
index 6b691fba2b86..406ae0a65e9e 100644
--- a/wlroots-sys/examples/basic.rs
+++ b/wlroots-sys/examples/basic.rs
@@ -77,10 +77,12 @@ impl Listener {
 }
 
 macro_rules! init_listener {
-    ($ptr:expr, $container:ty, $field:ident, $signal:expr, $handler:ident) => {{
-        extern "C" fn handle(l: *mut wl_listener, _: *mut c_void) {
+    ($ptr:expr, $container:ident, $field:ident, $signal:expr, $handler:ident) => {{
+        // TODO: add BackendHandler and D as macro params
+
+        extern "C" fn handle<H>(l: *mut wl_listener, _: *mut c_void) where H: BackendHandler {
             let ptr = unsafe {
-                let ptr = container_of!(l, $container, $field.listener) as *mut $container;
+                let ptr = container_of!(l, $container<H>, $field.listener) as *mut $container<H>;
                 ptr.as_mut().unwrap()
             };
             ptr.$handler();
@@ -90,7 +92,7 @@ macro_rules! init_listener {
             let l = $ptr
                 .as_mut()
                 .map_unchecked_mut(|container| &mut container.$field);
-            l.init($signal, handle);
+            l.init($signal, handle::<D>);
         }
     }};
 }
@@ -134,7 +136,7 @@ impl<D> Backend<D> where D: BackendHandler {
         });
         init_listener!(
             backend,
-            Backend<D>,
+            Backend,
             destroy,
             &mut ptr.events.destroy,
             handle_destroy