Remacs: Advantages of using Rust

Created on 20 Jul 2017  路  4Comments  路  Source: remacs/remacs

This can be labeled as a "Question" I think.

While I am sure, that there are advantages to using Rust for the emacs base in terms of safe code and in the future maybe even speed and development time, I am wondering in what areas exactly the advantages are, when rewriting the emacs base.

For example I just saw the following https://github.com/Wilfred/remacs/pull/255 .

It's great that people contribute, but should not anything unsafe be avoided at all cost if possible? Isn't that the purpose of rewriting the whole thing? And thus to avoid any bugs on that layer?

Maybe the Rust rewrite gets rid of very old cruft, which has accumulated. I don't know. I have no clue about the emacs core things and how the rewriting policies are or how the rewriting has been going so far. That is why I am writing this question.

Please tell in detail how Rust helps with the emacs core/base/whatever, which aspects will be improved once the rewrite is done ; )

Most helpful comment

In addition to the great answers above, let me clarify a little regarding unsafe.

Remacs will always have some unsafe code in it.

  • Elisp uses tagged pointers, which can only be dereferenced after you've checked the tag. It's easy to provide a safe interface on top, but this will always be unsafe.

  • Using the Emacs FFI is interacting with arbitrary C code, so that's unsafe.

  • The bytecode interpreter (written in C) deliberately contains some array access without range checks (for performance), assuming that Emacs always generates valid bytecode. Remacs will probably want to do the same thing.

  • I suspect the garbage collector (when ported to Rust) will contain some unavoidable unsafe bits.

Overall, we should be able to move to a codebase with very few unsafe blocks. At the moment however, the Rust code calls into the C code in many places, which requires unsafe blocks too.

All 4 comments

There's a few possible answers here, so let me present my personal view.

I would guess that most of us who work on Remacs do it because we love Rust and Emacs. So that's one motivation to choose this relatively large project (300kLOC of C) for a rewrite. It's also a very nice case study of how to transfer a code base piece by piece, hooking into the core layer (LispObjects) while keeping compatibility with existing C. Finally, it's an opportunity to use the strengths of the library ecosystem, and to discover and put into place missing pieces.

As long as there is substantial core infrastructure maintained on the C side, we have to make concessions. As soon as we completely control a core piece, we can make it more idiomatic (for example, if we can port the core of Emacs' hash tables, we can make it use the standard HashMap, see #251).

In any case, Rust is not only about avoiding unsafe code. It is also about collecting a small kernel of unsafe, making sure safety guarantees are upheld at the boundary, and abstracting it away for the consumers, as much as possible in a zero-cost way.

Regarding #255 in particular, there's nothing I can see that's especially unsafe there.

But yes, the goal is to avoid as many bugs as possible using the static guarantees of Rust. One very trivial but hopefully clear example of how we already do this is the API to cast the anytype LispObject to specific variants, let's say a float. Emacs C writes this as CHECK_FLOAT(x) followed by a float = EXTRACT_FLOAT(x), which will simply do something undefined when the CHECK was forgotten and you pass something different. In Rust, we have as_float() which returns None or Some(float), both of which cases must be handled.

To add to what @birkenfeld said, which was well said, I would like to quote a post I made on Hacker News about this subject:

I believe there are hidden benefits to an incremental rewrite to Rust executed this way.

  • First is that remacs has been able to reduce the amount of non-Elisp code in the editor needed to implement a number of different features. It was able to do this by replacing hand rolled C functionality with high quality crates from cargo, or code in the standard library. I'd attribute this to how easy the cargo system makes implementing libraries into your project.
  • Second is I believe that this project overall is increasing knowledge share of not just Rust, but also mainline emacs. I personally was intimidated by looking into contributing to mainline emacs. The remacs community was so friendly and welcoming I felt very encouraged to jump in and start working on things, and now I not only have better knowledge of Rust, but a pretty decent knowledge of the C layer in emacs and how it lays out and implements it's data structures etc.
  • Third is that the majority of the emacs codebase is written in emacs Lisp. Remacs aims to be fully Elisp compatible, and any differences are considered bugs and should be reported. This means that the project is still able to keep up feature and bug parity with the mainline, while incrementally rewriting the core of the editor.

In the long term, not only will we have safer code, but we will have less of it to maintain. This is not just because of using crates, but from being able to eliminate a lot of legacy code paths that aren't relevant anymore for our supported platforms.

Beyond general safety, I believe that Rust will give us a unique advantage in solving certain long term problems in the emacs community that have been difficult to address (in my opinion) partially because of the C language, such as threading key parts of emacs that are currently single threaded, or the unexec problem (https://github.com/Wilfred/remacs/issues/105).

In addition to the great answers above, let me clarify a little regarding unsafe.

Remacs will always have some unsafe code in it.

  • Elisp uses tagged pointers, which can only be dereferenced after you've checked the tag. It's easy to provide a safe interface on top, but this will always be unsafe.

  • Using the Emacs FFI is interacting with arbitrary C code, so that's unsafe.

  • The bytecode interpreter (written in C) deliberately contains some array access without range checks (for performance), assuming that Emacs always generates valid bytecode. Remacs will probably want to do the same thing.

  • I suspect the garbage collector (when ported to Rust) will contain some unavoidable unsafe bits.

Overall, we should be able to move to a codebase with very few unsafe blocks. At the moment however, the Rust code calls into the C code in many places, which requires unsafe blocks too.

I don't think this needs to be kept open.

Was this page helpful?
0 / 5 - 0 ratings