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.
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).
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.
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.
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.
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.
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));
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.