Borrow checker error when wrapping a C library

I am tryting to wrap a C library in rust, but I am still VERY new to the language. I use the following code:

extern crate libloading;


use libloading::{Library, Symbol};
use std::os::raw::{
    c_int};


type FirstFunc = unsafe fn(*mut c_int) -> c_int;
type SecondFunc = unsafe fn(c_int, *mut c_int) -> c_int;


pub struct LibWrapper<'a> {
    library: Library,
    first_: Symbol<'a, FirstFunc>,
    second_: Symbol<'a, SecondFunc>,
}

impl<'a> LibWrapper<'a> {
    pub fn new() -> Self {
        let library_path = "/home/username/libwrapperproject/libSomeLibrary.so".to_string();
        let lib: Library = unsafe { Library::new(library_path).unwrap() };
        let first: Symbol<FirstFunc> = unsafe { lib.get(b"first_").unwrap() };
        let second: Symbol<SecondFunc> = unsafe { lib.get(b"second_").unwrap() };

        Self {
            library: lib,
            first_: first,
            second_: second
        }
    }

    pub fn first(&self) -> () {
        let mut error: c_int = 0;
        unsafe {
            let errorr = &mut error as *mut c_int;

            (self.first_)(errorr);
        }
    }

    pub fn second(&self, input: i32) -> () {
        let mut error: c_int = 0;
        unsafe {
            let errorr = &mut error as *mut c_int;

            (self.second_)(input, errorr);
        }
    }
}


fn main() {
    println!("Hello, world!");

    let lw = LibWrapper::new();
    lw.first();
    lw.second(123);
}

I get the following compiler errors:

error[E0515]: cannot return value referencing local variable `lib`
  --> src/main.rs:26:9
   |
23 |           let first: Symbol<FirstFunc> = unsafe { lib.get(b"first_").unwrap() };
   |                                                   --- `lib` is borrowed here
...
26 | /         Self {
27 | |             library: lib,
28 | |             first_: first,
29 | |             second_: second
30 | |         }
   | |_________^ returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing local variable `lib`
  --> src/main.rs:26:9
   |
24 |           let second: Symbol<SecondFunc> = unsafe { lib.get(b"second_").unwrap() };
   |                                                     --- `lib` is borrowed here
25 | 
26 | /         Self {
27 | |             library: lib,
28 | |             first_: first,
29 | |             second_: second
30 | |         }
   | |_________^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `lib` because it is borrowed
  --> src/main.rs:27:22
   |
19 |   impl<'a> LibWrapper<'a> {
   |        -- lifetime `'a` defined here
...
23 |           let first: Symbol<FirstFunc> = unsafe { lib.get(b"first_").unwrap() };
   |                                                   --- borrow of `lib` occurs here
...
26 | /         Self {
27 | |             library: lib,
   | |                      ^^^ move out of `lib` occurs here
28 | |             first_: first,
29 | |             second_: second
30 | |         }
   | |_________- returning this value requires that `lib` is borrowed for `'a`

Some errors have detailed explanations: E0505, E0515.
For more information about an error, try `rustc --explain E0505`.
error: could not compile `libwrapper` due to 3 previous errors

I am not sure how to change the "lib" variable to an owned value. Any help would be much appreciated.

Thanks

Ruzter

Rust references are not general-purpose pointers, but a temporary permission to access values stored somewhere else. It's a specialized feature with limited applicability, and in 99% of structs, it's an error to use it.

struct Foo<'a> makes the struct usable only in the scope where 'a has been created. The data marked with 'a must outlive it, and there's no garbage collection, so the language will not make it live. That lifetime must be statically ensured by the structure of your program.

All variables are destroyed at the end of their scope. So if you tie a temporary reference to a variable, it won't be able to outlive it.

So you have no way of returning the struct from your new, because everything marked with 'a is tied to your first/second variables which are destroyed before new ends.

You have to take &'a Library by argument to new, so when it gives you first/second they will outlive the new call (I assume the get method is designed to be tied to Library's lifetime).

1 Like

I think that's overstating it a bit. I have quite a few structs with lifetimes. I think of references as pointers to things further up the call stack ( although this isn't precisely accurate as it could be something on the heap owned by something further up the call stack ). This seems to work for me.

Thanks for your replies, @kornel and @geebee22. This is a nice first experience on the Rust Programming Language Forum.

Let's consider the lib variable:

let lib: Library = unsafe { Library::new(library_path).unwrap() };

I expected that since ownership of memory can be transferred by assignment:

set lib_new_owner = lib;

and by passing the variable to a function:

some_function(lib);

ownership would also be transferred to the struct in the following way:

LibWrapper {
    library: lib,
    first_: first,
    second_: second
}

This is clearly not the case. Is there perhaps a way to transfer ownership of lib to the newly created struct?

Thanks again for the help.

Ruzter

It would indeed. The problem is that first and second are referring to the old owner, and not to the field in LibWrapper; therefore, when you move lib into LibWrapper, you invalidate these references.

The thing you're trying to do is, in fact, quite hard to do correctly and is outright impossible, if you stick to only safe Rust. The search keywords are "self-referential structs", if you want to know more.

3 Likes

If you put the Library inside the stuct, other common solution is using a RawSymbol instead a Symbol. Like:

3 Likes

Rust's change of ownership is semantically equivalent to a memcpy of the object to a new address. Therefore, as soon move happens, every older reference becomes invalid (pointing to the old address), so it must not be allowed to be used.

To solve this problem, taking a reference in Rust "freezes" the referenced object and prevents it from being moved until references are gone. This is unfortunately limiting when you want to return (move!) the object together with references to it. Borrow checker is unable to guarantee safety of this.

Owned (droppable) dynamically loaded Libraryes are one of the biggest footgun in Rust; try to only use it to store it within a OnceCell:

  let library_path = "/home/username/libwrapperproject/libSomeLibrary.so".to_string();
- let lib: Library = unsafe { Library::new(library_path).unwrap() };
+ let lib: &'static Library = {
+     static LIB: OnceCell<Library> = OnceCell::new();
+     LIB.get_or_insert(|| unsafe { Library::new(library_path).unwrap() })
+ };

and, from there:

pub struct LibWrapper {
    first: Symbol<'static, FirstFunc>,
    second: Symbol<'static, SecondFunc>,
}
1 Like

Thanks @Yandros. This is not a feasible solution for me.

The library that I have to load stores its data globally, and I need multiple instances of it to execute calculations in parallel. This means that I have to load the same library multiple times. For this reason, I have to store a reference to the library inside the struct, and I cannot store it in a static variable not associated with the struct.

Thanks @rubenrua.

This seems like a viable solution for my challenge. I will test this and confirm. If it all works out, I will mark your post as a solution.

Interesting, that's a less frequent use case. In that case I'd recommend to still use a "leaked Library" approach, given, again the danger of disposing of a Library while pseudo-'static data still refers to it:

  let library_path = "/home/username/libwrapperproject/libSomeLibrary.so".to_string();
  let lib: Library = unsafe { Library::new(library_path).unwrap() };
+ let lib: &'static Library = Box::leak(Box::new(lib));

and, from there:

pub struct LibWrapper {
    library: &'static Library,
    first: Symbol<'static, FirstFunc>,
    second: Symbol<'static, SecondFunc>,
}

Thanks again @rubenrua. I implemented this approach, and it is working well for me.

Many thanks again for your answer @Yandros. Since the suggestion from @rubenrua is working for me, I will not test your latest one at the moment, but I will keep it in mind for the future.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.