]> piware.de Git - learn-rust.git/blob - call-rust-from-c/src/lib.rs
Call Rust function from C: Complex cases
[learn-rust.git] / call-rust-from-c / src / lib.rs
1 use std::ffi::{CStr, CString};
2 use std::os::raw;
3
4 /// Return The Answer.
5 ///
6 /// # Example
7 ///
8 /// ```
9 /// let a = call_rust_from_c::answer();
10 /// assert_eq!(a, 42);
11 /// ```
12 #[no_mangle]
13 // C 'int'; it does not actually hurt to have more specific types such as int32_t, but it's good to know that plain ints work
14 pub extern "C" fn answer() -> raw::c_int {
15     return 42;
16 }
17
18 #[no_mangle]
19 pub extern "C" fn r_strlen(s: *const raw::c_char) -> usize {
20     unsafe { CStr::from_ptr(s) }.to_bytes().len()
21 }
22
23 /// Return a vector of pointers as C pointer array
24 ///
25 /// The memory of `vec` gets leaked, as otherwise Rust would free it once the vector
26 /// goes out of scope, and C would access invalid memory.
27 fn return_c_vec<T>(mut vec: Vec<T>) -> *mut T {
28     let p = vec.as_mut_ptr();
29     // unref vector so that it does not get freed when going out of scope
30     std::mem::forget(vec);
31     p
32 }
33
34 #[no_mangle]
35 pub extern "C" fn r_strlist() -> *mut *const raw::c_uchar {
36     let v = vec![
37         "Hello\0".as_ptr(),
38         "World\0".as_ptr()
39         ];
40     return_c_vec(v)
41 }
42
43 fn impl_grep<'a>(needle: &str, haystack: &'a str) -> Vec<&'a str> {
44     haystack.lines()
45         .filter(|line| line.contains(needle))
46         .collect()
47 }
48
49 #[no_mangle]
50 pub extern "C" fn r_grep(needle: *const raw::c_char, haystack: *const raw::c_char) -> *mut *const raw::c_char {
51     let haystack_cstr = unsafe { CStr::from_ptr(haystack).to_str() }.unwrap();
52     let strvec = impl_grep(unsafe { CStr::from_ptr(needle).to_str().unwrap() }, haystack_cstr);
53
54     // Vec[str] -> Vec[const char*]
55     let p_vec: Vec<_> = strvec.into_iter()
56         .map(|s| {
57             let s = CString::new(s).unwrap();
58             let p = s.as_ptr();
59             std::mem::forget(s);
60             p
61         })
62         .collect();
63
64     return_c_vec(p_vec)
65 }
66
67 #[cfg(test)]
68 mod tests {
69     use super::*;
70
71     #[test]
72     fn test_answer() {
73         assert_eq!(answer(), 42);
74     }
75
76     #[test]
77     fn test_grep() {
78         assert_eq!(impl_grep("ell", "Hello\nworld\ncan you tell?"), vec!["Hello", "can you tell?"]);
79     }
80 }