extendr / rextendr

An R package that helps scaffolding extendr-enabled packages or compiling Rust code dynamically
https://extendr.github.io/rextendr/
Other
184 stars 27 forks source link

Is it possible to use external crates in fenced block? #12

Closed molpopgen closed 3 years ago

molpopgen commented 3 years ago

Thanks for this tool! I am trying to write a document using R markdown + rust. I am stuck when trying to write a code block using an external crate and cannot find any examples. Is this currently not possible?

My example:

# Test

```{r setup}
library(rextendr)

Success

pub struct TestStruct {
    x: i32,
    y: String,
}

Failure

How to do this?

use tskit_rust;
let mut tables = tskit_rust::TableCollection::new(1000.).unwrap();
clauswilke commented 3 years ago

This is in the very early stages. I literally wrote it this week. So things may not work yet or documentation is missing.

I would recommend you first try to get things working with rust_eval() and rust_source(). Once your code works in those contexts you can try to migrate to R Markdown. An extendr chunk calls rust_eval() and an extendrfuns chunk calls rust_source(). To work with use statements you need to use an extendrfuns chunk. You also need to update the dependencies. See the example in the Readme:

code <- r"(
use pulldown_cmark::{Parser, Options, html};

#[extendr]
fn md_to_html(input: &str) -> Robj {
    let mut options = Options::empty();
    options.insert(Options::ENABLE_TABLES);
    let parser = Parser::new_ext(input, options);
    let mut output = String::new();
    html::push_html(&mut output, parser);
    Robj::from(&*output)
}
)"
rust_source(code = code, dependencies = 'pulldown-cmark = "0.8"')
clauswilke commented 3 years ago

It should be possible to set the dependencies from R Markdown via a chunk option, such as:

```{extendrfuns engine.opts = list(dependencies = 'pulldown-cmark = "0.8"')}

but I haven't tried that yet.

molpopgen commented 3 years ago

It should be possible to set the dependencies from R Markdown via a chunk option, such as:



but I haven't tried that yet.

A related thing works:

```{extendr engine.opts = list(dependencies = 'tskit_rust = "0.1.0"')}
use tskit_rust;
let mut tables = tskit_rust::TableCollection::new(1000.).unwrap();
let rv = tables.add_node(0, 0.0, tskit_rust::TSK_NULL, tskit_rust::TSK_NULL).unwrap();


I'll let you decide re: closing this or keeping open until documented?
clauswilke commented 3 years ago

It's documented now: https://extendr.github.io/rextendr/articles/rmarkdown.html#exporting-rust-functions-to-r-1

I'm glad to know that it works inside simple extendr chunks also. Those chunks wrap everything into a Rust function, so you can only do things that you can do inside a function body, but apparently that's almost anything, including use statements.

molpopgen commented 3 years ago

Those chunks wrap everything into a Rust function, so you can only do things that you can do inside a function body, but apparently that's almost anything, including use statements.

So this perhaps explains the other problem I am having, which is persistence. I tried to define a struct, write a markdown paragraph, and then use the struct in a later code fence. That failed because the downstream fence was unaware of the struct.

CGMossa commented 3 years ago

impl-blocks can be inserted into functions; that's no issue. But type definitions doesn't "persist". They have to be part of the module definition.I hacked together some code here: play.rust-lang.org chunk example

molpopgen commented 3 years ago

@CGMossa -- yes, that's just regular rust. But I'm referring to using markdown for a "report" or "book": here's a function (code). Explain the use case. then use it (more code).

For example, this is supported by most Rmd engines:

```{r setup}
library(rextendr)

Here's a type we may want to work with.

pub struct TestStruct {
    x: i32,
    y: String,
}

More prose.

Definitions are not persistent as expected, and this fails:

let t = TestStruct{ x: 1, y: "boo!".as_string() };
rprintln!("{} {}", t.x, y.y);
clauswilke commented 3 years ago

@molpopgen Can you do the equivalent with Rcpp? At present, each extendr chunk is a separate compilation unit. One workaround could be to allow the user to inject some preamble code that is added to a chunk but not shown. Or, alternatively, somehow chain chunks together, so that rextendr knows to collect the previous code chunks and compile again.

If this is a feature you find important, please open a separate issue.