// csrc/ccode01.c
#include<stdio.h>
typedef void (*SumSquareCB)(int result);
void sum_square_cb01(int a, int b, SumSquareCB cb) {
int result = a*a + b*b;
cb(result);
}
// src/r01.rs
use std::os::raw::c_int;
pub type SumSquareCB = unsafe extern fn(c_int);
#[link(name = "ccode01")]
extern {
pub fn sum_square_cb01(a: c_int, b: c_int, cb: SumSquareCB);
}
pub unsafe extern fn cb_func(result: c_int) {
println!("The result in callback function is: {}", result);
}
fn main() {
unsafe {
sum_square_cb01(3, 4, cb_func);
}
}
typedef void (*SumSquareCB)(int result);
pub type SumSquareCB = unsafe extern fn(c_int);
fn 是 Rust 中的函数指针类型。具体可参见标准库文档 fn,解释得非常详尽。函数指针的功能就是指向函数代码片断,可以用函数指针来调用函数,效果跟函数名一样,如上面 C 代码中的 cb(result)。
pub unsafe extern fn cb_func(result: c_int) {
println!("The result in callback function is: {}", result);
}
RUSTFLAGS='-L .' LD_LIBRARY_PATH="." cargo run --bin r01
The result in callback function is: 25
// src/r01-1.rs
use std::os::raw::c_int;
pub type SumSquareCB = unsafe extern fn(c_int);
#[link(name = "ccode01")]
extern {
pub fn sum_square_cb01(a: c_int, b: c_int, cb: SumSquareCB);
}
fn main() {
let mut sum = 0;
pub unsafe extern fn cb_func(result: c_int) {
sum += result;
}
unsafe {
sum_square_cb01(3, 4, cb_func);
}
println!("The result in callback function is: {}", sum);
}
error[E0434]: can't capture dynamic environment in a fn item
--> src/r01-1.rs:14:9
|
14 | sum += result;
| ^^^
|
= help: use the `|| { ... }` closure form instead
error: aborting due to previous error
// src/r01-2.rs
use std::os::raw::c_int;
pub type SumSquareCB = unsafe extern fn(c_int);
#[link(name = "ccode01")]
extern {
pub fn sum_square_cb01(a: c_int, b: c_int, cb: SumSquareCB);
}
fn main() {
let mut sum = 0;
unsafe {
sum_square_cb01(3, 4, |r| sum += r );
}
println!("The result in callback function is: {}", sum);
}
error[E0308]: mismatched types
--> src/r01-1.rs:14:31
|
14 | sum_square_cb01(3, 4, |r| sum += r );
| ^^^^^^^^^^^^ expected fn pointer, found closure
|
= note: expected fn pointer `unsafe extern "C" fn(i32)`
found closure `[closure@src/r01-1.rs:14:31: 14:43 sum:_]`
error: aborting due to previous error
void * 携带一个数据块传递。了解过 void * 的就知道,它和 C 中的其它指针一起,几乎把 C 变成了一门动态语言(所以有一种说法认为 C 其实是弱类型语言?)。void * 是一种通用指针,意思是”指向某个东西的指针“,它的灵活和强大之处在于,可以强制转换到任何指针类型。这里,我们也可以使用 void * 来传递我们的”数据块“。// csrc/ccode02.c
#include<stdio.h>
typedef void (*SumSquareCB)(int result, void *user_data);
void sum_square_cb02(int a, int b, SumSquareCB cb, void *user_data) {
int result = a*a + b*b;
cb(result, user_data);
}
use std::os::raw::c_int;
use std::ffi::c_void;
pub type SumSquareCB = unsafe extern fn(c_int, *mut c_void);
#[link(name = "ccode02")]
extern {
pub fn sum_square_cb02(a: c_int, b: c_int, cb: SumSquareCB, user_data: *mut c_void);
}
pub unsafe extern fn cb_func(result: c_int, user_data: *mut c_void) {
let data = &mut *(user_data as *mut c_int);
*data += result;
}
fn main() {
let mut sum = 0;
unsafe {
sum_square_cb02(
3,
4,
cb_func,
&mut sum as *mut c_int as *mut c_void);
}
println!("The sum is {}", sum);
}
RUSTFLAGS='-L .' LD_LIBRARY_PATH="." cargo run --bin r01
The sum is 25
std::ffi::c_void;。这是 Rust 给我们提供的强大的基础设施,不然我们真要愁眉苦脸了。从标准库页面可以学习到,Rust 中的 *const c_void 等于 C 的 const void*,Rust 中的 *mut c_void 等于 C 的 void*。(C 中的 void 函数返回值本身,与 Rust 的空值类型 () 相等)void * 与 *mut c_void 的写法和对应关系。pub unsafe extern fn cb_func(result: c_int, user_data: *mut c_void) {
let data = &mut *(user_data as *mut c_int);
*data += result;
}
void * 的强大就在于可以与任意指针类型进行强制转换。在 Rust 也是对应的,也可以与任意指针类型强制转换。let data = &mut *(user_data as *mut c_int);
*mut c_void 指针转换成 *mut c_int 指针,然后用 * 取它的数据块,然后用 &mut 取这个数据块的可变引用,进入 Rust 的常规领域(标准使用模式)。*data += result;
unsafe {
sum_square_cb02(
3,
4,
cb_func,
&mut sum as *mut c_int as *mut c_void);
}
&mut sum as *mut c_int as *mut c_void
&mut sum(sum 的可变引用),转换成 *mut c_int(c_int 类型的指针),进而转换成 *mut c_void(通用指针)。#include<stdio.h>
typedef void (*SumSquareCB)(int result, void *user_data);
void sum_square_cb03(int a, int b, SumSquareCB cb, void *user_data) {
int result = a*a + b*b;
cb(result, user_data);
}
use std::os::raw::c_int;
use std::ffi::c_void;
pub type SumSquareCB = unsafe extern fn(c_int, *mut c_void);
#[link(name = "ccode03")]
extern {
pub fn sum_square_cb03(a: c_int, b: c_int, cb: SumSquareCB, user_data: *mut c_void);
}
pub unsafe extern fn cb_func(result: c_int, user_data: *mut c_void) {
let data = &mut *(user_data as *mut SumRecord);
data.sum += result;
data.elem_number += 1;
}
#[derive(Debug, Default, Clone, PartialEq)]
struct SumRecord {
sum: c_int,
elem_number: usize,
}
fn main() {
let mut sum = SumRecord::default();
unsafe {
sum_square_cb03(
3,
4,
cb_func,
&mut sum as *mut SumRecord as *mut c_void);
}
println!("The sum is {:?}", sum);
}
RUSTFLAGS='-L .' LD_LIBRARY_PATH="." cargo run --bin r03
The sum is SumRecord { sum: 25, elem_number: 1 }
#[derive(Debug, Default, Clone, PartialEq)]
struct SumRecord {
sum: c_int,
elem_number: usize,
}
let data = &mut *(user_data as *mut SumRecord);
data.sum += result;
data.elem_number += 1;
*mut c_int 变成了 *mut SumRecord 了。然后,更新数据的时候,按 Rust 结构体更新的方式操作就可以了。unsafe {
sum_square_cb03(
3,
4,
cb_func,
&mut sum as *mut SumRecord as *mut c_void);
}
原文作者:Rust语言中文社区;如有侵权,请联系管理员删除。