Hi, Artichoke author here. I feel very blessed to have Artichoke be mentioned in this ticket on Ruby's bug tracker. If y'all are interested to follow along, @artichokeruby on Twitter is the best spot to do it.
The compiler does have a lint to force being explicit about unsafe in the way you describe. It is an allow by default lint called `unsafe_op_in_unsafe_fn`.
Code size in an embedded context is one use case for the modularity here. Another is limiting access of Ruby code to the host system.
For example, one may wish to use Artichoke in a game engine for scene scripting. It’s probably undesirable for Ruby code run in this context to modify the host environment, so `ENV` can be disabled at compile time. But maybe you have existing code that uses `ENV`. Toggle a compile time flag and you can get a compatible `ENV` implementation that is backed by a hash map.
You might be interested in this early work to build a Sinatra echo server [0] with Artichoke (called ferrocarril back then). `hubris` used a custom Rack-compatible web server called `nemesis` [1] based on Rocket [2], a Rust Flask-like web framework.
Thanks for catching this. That is spot on. These APIs in the `Symbol` backend [0] show this concept off really well I think.
The `Symbol` data structure knows how to, e.g. render its `Symbol#inspect` implementation [1]. To extract the underlying bytes associated with the `Symbol`, however, it needs an interpreter. But not a whole interpreter or any particular interpreter, just one that implements `Intern` [2].
All of these generics get monomorphized so there are no vtables or other indirection. Because it's unlikely you'll have more than one interpreter implementation in your program, there is no code bloat either.
`mem::transmute` is roughly equivalent to a `reinterpret_cast` in C++. It treats the bits of a u8 as an i8.
In the Rust definition of safety (mutable xor shared, no data races, memory safety, etc), treating the bits of a u8 as an i8 is safe and can be done with an `as` cast.
in the 14 hours since I wrote the comment you replied to, I moved an MRI C API from non-goal to goal [0]. :) The red X means the goal is not yet achieved.
For an example of how Artichoke can be faster than MRI, I'm currently working on extending the oniguruma-based `Regexp` implementation to have a fast-path backed by the Rust regex crate in some circumstances. In testing this can speed up `String#scan` by a factor of 10 for some `Regexp`s
Growing pains of blowing up on HN lol, I do not have this written down yet, but I have a ticket to start documenting this [0].
The idea is to have a core set of traits [1] that when implemented allow an implementation to load an interpreter agnostic Ruby core and Ruby Standard Library.
There is currently only one interpreter that implements these traits, and it is backed by mruby. My ultimate goal is to move off of mruby to either an MRI-backed interpeter via rutie or a native Rust-implemented backend + VM + parser.
I can run MSpec to completion for language, core, and library specs. Thank you for the encouragement :D.
I've implemented a runner that can skip known things that cause the spec to hang (e.g. Mutex specs since Artichoke has a single-threaded implementation of Mutex that deadlocks during the specs).
$ pushd spec-runner/vendor/ruby/language
$ cargo run --bin spec-runner **/*.rb
Passed 1078, skipped 35, not implemented 2, failed 476 specs.
$ popd; pushd spec-runner/vendor/ruby/core
$ cargo run --bin spec-runner **/*.rb
Passed 5412, skipped 998, not implemented 201, failed 8629 specs.
$ popd; pushd spec-runner/vendor/ruby/library
$ cargo run --bin spec-runner **/*.rb
Passed 0, skipped 22, not implemented 16, failed 3276 specs.
The library specs pass number is a bug in my spec runner. All packages added to Artichoke pass ruby/spec. Those packages are : delegate, forwardable, json, monitor, ostruct, set, srscan, and uri.
https://twitter.com/artichokeruby