Well explained! Here are some more examples using the calllib function to access the rust code, although I think the mex interface is probably the better way to go.
https://github.com/ampron/rustlab
One thing that jumped out to me, and I haven't worked with Rust FFI enough to know this answer. Is the C library necessary? Since Rust is exporting the C interfaces directly, couldn't Matlab link directly against that?
It would also require generating the Matlab bindings for Rust, but more problematically it seems to use a special compiler/linker (`mex`). I don't know if that does genuine work of if it's just a wrapper/synchronizer around compilers and linkers which you could do without.
The main thing mex does is let you link to the matlab API libraries that are otherwise hard to get. To work around this, I like to just grab the API directly from the calling process via dlsym/GetProcAddress. That way, you can build mex libraries with any toolchain you want, even without matlab installed.
This can be non-portable because the mex header will expand certain function calls to specific implementations depending on the mex options you use. I seem to recall e.g. mxGetM expanded to mxGetM700 in one version.
One minor suggestion, completely beside the point so please forgive me: a safer and perhaps more idiomatic way to implement the multiply_safe function would be with a functional-style one-liner:
fn multiply_safe(a : Vec<f64>, b : Vec<f64>) -> Vec<f64> {
if a.len() != b.len() {
panic!("The two vectors differ in length!");
}
return a.iter().zip(b.iter()).map(|(x, y)| x * y).collect()
}
Safer because you don't manipulate indices and lengths directly like you would in C, so there is no opportunity of an off-by-one bug or things like that. Surprisingly, rustc optimizer makes sure that this performs as well as the index-wrangling implementation (according to some simple tests I just ran).
> Surprisingly, rustc optimizer makes sure that this performs as well as the index-wrangling implementation (according to some simple tests I just ran).
Frequently functional style optimizes better than the equivalent loop and index implementations because the iterator functional implementation cannot go out of bounds on the array, so the bounds checks that are implicit in indexing are elided.
There are plenty of other things to improve/rustify.
* The function should take slices as inputs for instance, that way not only doesn't it take ownership of the inputs you don't need to copy the C arrays to vecs in the C-exposed function, you can just convert them to slice (which is essentially 0-cost, you just create a std::raw::Slice on the stack)
* The function also shouldn't panic it should return a Result (or an Option)
* Maybe less rustic but more efficient would be to take the output as an &mut [f64] (as the extern function does), that way the whole thing can be allocation-free
1 and 3 also simplify the glue code, instead of having to copy data from the input f64 to brand new vecs and from the output vec to the output f64, just convert the three input pointers with from_raw_parts and from_raw_parts_mut. That makes for much simpler and easier to review unsafe code as well:
let a = unsafe { from_raw_parts(a_double, elements) };
let b = unsafe { from_raw_parts(b_double, elements) };
let c = unsafe { from_raw_parts_mut(c_double, elements) };
that's it, no mucking around with pointer offsets, and the rest if safe Rust.
Interesting, though the C middleman isn't necessary if you can describe to Rust's FFI that mxArray is an opaque struct (I imagine the other regular functions in the MEX API are cakewalk for an FFI worth it's weight).
A while ago I wrote a mexFunction purely in Cython, and it was wondeful because Python was immediately available to work with (instead of C). The problem was that the numerical libraries on which NumPy builds often shared symbol names with MATLAB (such as svd, or hdf5) that differ in implementation resulting segfaults or corrupted data.
For the non-MATLABers, a mex function is just a small shared library which exports a single function 'mexFunction'. It's dynamically loaded by MATLAB when called in the MATLAB language.
11 comments
[ 2.2 ms ] story [ 33.7 ms ] threadOne minor suggestion, completely beside the point so please forgive me: a safer and perhaps more idiomatic way to implement the multiply_safe function would be with a functional-style one-liner:
Safer because you don't manipulate indices and lengths directly like you would in C, so there is no opportunity of an off-by-one bug or things like that. Surprisingly, rustc optimizer makes sure that this performs as well as the index-wrangling implementation (according to some simple tests I just ran).Frequently functional style optimizes better than the equivalent loop and index implementations because the iterator functional implementation cannot go out of bounds on the array, so the bounds checks that are implicit in indexing are elided.
* The function should take slices as inputs for instance, that way not only doesn't it take ownership of the inputs you don't need to copy the C arrays to vecs in the C-exposed function, you can just convert them to slice (which is essentially 0-cost, you just create a std::raw::Slice on the stack)
* The function also shouldn't panic it should return a Result (or an Option)
* Maybe less rustic but more efficient would be to take the output as an &mut [f64] (as the extern function does), that way the whole thing can be allocation-free
1 and 3 also simplify the glue code, instead of having to copy data from the input f64 to brand new vecs and from the output vec to the output f64, just convert the three input pointers with from_raw_parts and from_raw_parts_mut. That makes for much simpler and easier to review unsafe code as well:
that's it, no mucking around with pointer offsets, and the rest if safe Rust.Something like this: http://is.gd/uFhx2J
A while ago I wrote a mexFunction purely in Cython, and it was wondeful because Python was immediately available to work with (instead of C). The problem was that the numerical libraries on which NumPy builds often shared symbol names with MATLAB (such as svd, or hdf5) that differ in implementation resulting segfaults or corrupted data.