As a long time Mozillian I too got caught up in the excitement of Rust. Almost every other day someone posts a Rust success story or how they fell in love with this wonderful language. Can I sit still while a language evolves? A while back, I wanted to write extensions for R using Rust. But till now Rust depended on several libraries being loaded and could not easily be called from other languages. That has all changed and embedding Rust is now trivial.

My time with Rust is a combination of basic programming, a lot of wonderful help on #rust (irc) and good Googling. I will be honest and say I havent grokked the memory model. I could not explain why I did some things and why not. And I certainly cannot correct the programs of others. That said, the compiler messages are wonderful and if a Rust program compiles, it will likely work. This is very relaxing and reassuring. Also Rust is cutting edge: there are many advances being made in the Rust world all of which the extension author can rely on.

Other things I enjoyed:

  • the macro system is powerful and fun to program with (but i like TerraLang’s better). Simplifying API design is very import.
  • the build system is superb (see the Github repo for build.rs).
  • I’m in love with the match approach to case matching.
  • Traits are great.
  • Though not a substitute for the Rust book (and you do need to be somewhat familiar with Rust to benefit from this), the link is a lucid introduction to the memory model and Rust signatures.

Though my next project will be a Rust one, I do wish for an excellent book like the K&R C book. The documentation is good, but slightly brief and Stackoverflow, though a gift, is not enough.

My still favorite language for extensions is RTerra which uses TerraLang. This enabled the R programmer to write extensions in Lua+Terra. It is a quite a pleasure to code and an easy language to learn (less than a day). But on OSX, one needs to compile R to use it (LuaJit restrictions). That is a show stopper. The other approach (which I will soon implement) is for the package to deliver an R executable which you would execute as R CMD rterra. But that means, if you use RevolutionR, then you lose out on the wonderful Terra.

I’ve decided to not do any more Rust-R coding. I doubt anyone is going to use it so I’ll leave the code up on GitHub here. Meanwhile some code samples.

You wrap the Rust code with a no mangle declaration. Rust like C++ will mangle function names and if that occurs you wont be able to call it from R. As you can see the code below just returns it’s parameter.

#[no_mangle]
pub extern fn ex0(p0 : SEXP) ->  SEXP {
        return p0;
}

This creates a new numeric vector of length 10. Garbage collection (R’s PROTECT/UNPROTECT) is taken care for you. You have to call to_sexp to return the SEXP version of the R - Rust object. Using Rust compiler plugins, one way would be declare the function to return T: RObject and the library would automatically call to_sexp

#[no_mangle]
pub extern fn exa(_: SEXP) -> SEXP {
        let p1 = realvec![_;10]; // numeric(10)
        return p1.to_sexp();
}

Pre create a numeric vector

#[no_mangle]
pub extern fn exb(_: SEXP) -> SEXP {
        //c(10.1,12.1,13.0,14.0)
        let  p3 = realvec![10.1,12.1,13.0,14.0]; 
        return p3.to_sexp();
}

Equivalent of R’s rep function. Also demonstrates that array indexing is nicely possible (with bounds checking that result in a panic)

#[no_mangle]
pub extern fn exd(_: SEXP) -> SEXP {
        let mut p2 = realvec![12.0;10]; // rep(12,10)
        p2[2] = 13.0;
        return p2.to_sexp();
}

Nice scalar multipliers and additions. Note the &p2. The ampersand indicates that the for loop is borrowing p2. Thus we can still use it (in the last line). Had we not used the ampersand, the for loop takes over ownership (of p2) and we can’t use p2 again. Learn about Rusts memory model to better understand this.

#[no_mangle]
pub extern fn exe(p0: SEXP) -> SEXP {
        let mut p2 = realvec![2.0;3]; // rep(2.0,3)
        p2[2] = 1.0;
        p2 = 2.0-3.0*p2;
        for x in &p2{
                println!("{}",x);
        }
        p2.to_sexp()
        
}

Here we demonstrate the R equivalent of slicing e.g. vector[ n1 : n2 ]. We create a vector of length 12, and rrange is a Rust macro which takes everything up to the 10’th element (not inclusive). We could also have done 10=> or even 2 => 10 (with the obvious interpretations). The underscore in the parameters indicates to Rust we don’t care about it and so the compiler will not emit unused variable warnings.

#[no_mangle]
pub extern fn ex3(_ : SEXP) ->  SEXP {
        let mut p = realvec![12.0;10]; 
        let s = rrange![p; => 10];
        println!("{}",p);
        return p.to_sexp();
}

So you coerce SEXP objects using code similar to below. But since a NULL cannot be converted into a numeric, we return a Result which could either be an error or an object that implements Robject. The coder will have to check for it. This can be good and bad. Good because it forces the coder to consider that the user might (and well can) have given incorrect types of arguments. The code is verbose and hence the bad. But this can be shortened using try!. Note how we use match to check the return type. If the conversion was a success, we get an Ok which contains a mutable reference to the object p.

We also check for the Error ( and with_sexp returns Result<Robject, RUnexpectedtype> , so we have exhaustively checked both values) and get a variable to the error directly inside the match. Modern languages like Haskell, Scala, Ocaml, F# and Rust have this superb matching system which once used is difficult to let go of.

#[no_mangle]
pub extern fn ex2(p0 : SEXP) ->  SEXP {
   match Rnumeric::with_sexp(p0) {
         Ok(mut p) => {
            println!("{}",p);
            // return p.to_sexp();
            p[0] = 10.2;
            return  Rnumeric::from(p[0] as f64).to_sexp();
        },
        Err(RUnexpectedType { rtype:t}) => {
           println!("Found Wrong Type: {:?}",t);
           return Rnull::to_sexp();
        }
   }
}

And here is the GitHub repository.