├── LICENSE ├── Makefile ├── README.md ├── fact.rs ├── main.hs └── point.rs /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | RC = LD_LIBRARY_PATH=/usr/local/lib rustc 2 | GHC = ghc 3 | 4 | all: main 5 | 6 | libfact.a: fact.rs 7 | $(RC) --crate-type staticlib fact.rs 8 | 9 | libpoint.a: point.rs 10 | $(RC) --crate-type staticlib point.rs 11 | 12 | main: libfact.a libpoint.a main.hs 13 | $(GHC) main.hs libfact.a libpoint.a -lpthread -o main 14 | 15 | clean: 16 | rm -f libfact.a libpoint.a main.hi main.o main 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust-Haskell FFI Example 2 | 3 | A lot of Haskell people I've talked to are excited about the prospect of 4 | using Rust as a replacement for C in certain speed-critical or low-level 5 | parts of their application. To that end, and in light of the recent 6 | (as of this writing) Rust 1.0 alpha release, I've shown here a small 7 | example of calling Rust from Haskell. 8 | 9 | This contains a single Haskell file and two Rust libraries which Haskell 10 | can call out to. 11 | The first Rust library is contained in `fact.rs` and implements a simple 12 | factorial, which is easier to wrap; the second is contained in `point.rs` 13 | and demonstrates allocating memory in Rust, passing it to Haskell, using 14 | wrapped Rust functions to manipulate it, and finally allowing Haskell's 15 | GC to call back into Rust to free it. 16 | 17 | This of course requires GHC and a reasonably recent version of rustc 18 | installed. This version has been tested with GHC versions `7.8.4` and 19 | `7.10.1`, and the following rustc versions: 20 | 21 | 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14) 22 | 23 | All the examples here I release into the public domain. 24 | -------------------------------------------------------------------------------- /fact.rs: -------------------------------------------------------------------------------- 1 | #![crate_type = "lib"] 2 | 3 | #[no_mangle] 4 | pub extern fn fact(x: u64) -> u64 { 5 | match x { 6 | 0 => 1, 7 | _ => x * fact(x-1), 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ForeignFunctionInterface #-} 2 | 3 | import Foreign 4 | import Foreign.C.Types 5 | import Foreign.ForeignPtr 6 | 7 | -- Wrapping the fact.rs module... 8 | foreign import ccall "fact" 9 | c_fact :: CULong -> CULong 10 | 11 | fact :: Int -> Int 12 | fact = fromIntegral . c_fact . fromIntegral 13 | 14 | -- A little bit more work to wrap the point.rs module; I also 15 | -- don't bother to write a proper representation for the 16 | -- underlying Point, which ought to be an instance of Storable 17 | -- if we want to manipulate it in Rust-land or do pointer 18 | -- arithmetic. 19 | type Point = ForeignPtr PointRepr 20 | data PointRepr 21 | 22 | foreign import ccall safe "mk_point" 23 | mk_point :: CULong -> CULong -> IO (Ptr PointRepr) 24 | 25 | foreign import ccall safe "add_point" 26 | add_point :: Ptr PointRepr -> Ptr PointRepr -> IO () 27 | 28 | foreign import ccall safe "print_point" 29 | print_point :: Ptr PointRepr -> IO () 30 | 31 | foreign import ccall safe "&free_point" 32 | free_point :: FunPtr (Ptr PointRepr -> IO ()) 33 | 34 | -- We use the free_point function as a finalizer when we 35 | -- create a point; this lets the Haskell GC take over for 36 | -- us. 37 | mkPoint :: Int -> Int -> IO Point 38 | mkPoint x y = do 39 | ptr <- mk_point (fromIntegral x) (fromIntegral y) 40 | newForeignPtr free_point ptr 41 | 42 | -- This modifies the first point in-place. 43 | addPoint :: Point -> Point -> IO () 44 | addPoint x y = withForeignPtr x (\ x' -> 45 | withForeignPtr y (\ y' -> 46 | add_point x' y')) 47 | 48 | -- This prints its argument 49 | printPoint :: Point -> IO () 50 | printPoint p = withForeignPtr p print_point 51 | 52 | -- Testing the point functions; we don't need to free 53 | -- explicitly because GHC should call our finalizer 54 | points :: IO () 55 | points = do 56 | a <- mkPoint 2 3 57 | b <- mkPoint 1 1 58 | addPoint a b 59 | printPoint a 60 | 61 | -- And here we go! 62 | main :: IO () 63 | main = do 64 | putStr "fact(5) = " 65 | print (fact 5) 66 | points 67 | -------------------------------------------------------------------------------- /point.rs: -------------------------------------------------------------------------------- 1 | #![crate_type = "lib"] 2 | 3 | // We aren't exposing the internals of this, so I don't bother giving it 4 | // a C representation. 5 | pub struct Point { 6 | x: u64, 7 | y: u64, 8 | } 9 | 10 | // Rust-land functions... 11 | impl Point { 12 | fn new(x: u64, y: u64) -> Point { 13 | Point { x: x, y: y } 14 | } 15 | 16 | fn add_mut(&mut self, p: &Point) { 17 | self.x = self.x + p.x; 18 | self.y = self.y + p.y; 19 | } 20 | } 21 | 22 | // What we're doing here is a little bit unsafe---by exposing 23 | // these elsewhere, we effectively transfer the 'ownership' out 24 | // of Rust-land. In this case, we'll have a corresponding free 25 | // function which takes ownership back again. 26 | #[no_mangle] 27 | pub extern fn mk_point(x: u64, y: u64) -> Box { 28 | Box::new(Point::new(x, y)) 29 | } 30 | 31 | // We free something in Rust by… taking ownership of it and doing nothing. 32 | #[no_mangle] 33 | pub extern fn free_point(_: Box) {} 34 | 35 | // Wrap our add_mut method for exporting. 36 | #[no_mangle] 37 | pub extern fn add_point(p1: &mut Point, p2: &Point) { 38 | p1.add_mut(p2); 39 | } 40 | 41 | // Wrap our print method for exporting. 42 | #[no_mangle] 43 | pub extern fn print_point(p: &Point) { 44 | println!("Point(x={}, y={})", p.x, p.y); 45 | } 46 | --------------------------------------------------------------------------------