4 ms·
A few weeks ago I tried to implement a lisp in Rust. Things were going fine until I decided that instead of parsing a string, I'd like to parse some kind of inp
by claudiawerner 8y ago
A few weeks ago I tried to implement a lisp in Rust. Things were going fine until I decided that instead of parsing a string, I'd like to parse some kind of input stream, like one would have in python with a "with open()"ing a file, or in C with a file pointer. The guys on #rust were very helpful with figuring that out (using a mutable iterator). Only after implementing did I find out that Rust dosen't seem to have support for reading files as iterators, and I'd have to write that myself. Okay, not so much of a problem.
Then came what I thought should be a simple task; since my code was mostly recursive, I wanted to add and remove from a map containing the program's current binding state, so when the evaluator sees a 'let' block:
(let ((a 1) (b 2))
(+ a b))
for instance, it evaluates the addition with a Map like: {'a':1, 'b':2}, and once that's finished evaluating, the evaluator returns from that scope and we're back to having nothing bound. After all, I don't want to access 'a' and 'b' outside my let.
As it turns out, Rust doesn't support using maps (or hash maps) in this way. You need to allocate and borrow them, and you can't borrow them in multiple places, so I couldn't have a function that checks whether something is bound, since I'm already borrowing the map in my evaluator function.
I was left asking: why can I do this in C (or another 'low level' language, passing the bindings as a pointer to array of a k-v struct that I simply swap with another and remember to free()) and do it in Python (or another 'high level' language like Haskell, creating the map anonymously when calling eval recursively), but not in Rust? The language seems to be somewhat focused on ideas of immutability, but I can't do the things that immutability lets me.
Other things seemed to get in the way too. Once you've 'match'ed a variable you can't actually deal with the thing you matched, because you've already borrowed it when you did the matching. In Haskell (case statement) this is no problem at all:
is_cons e =
case (car e) of
Nothing -> False
Just _ -> case (cdr e) of -- still matching on e
Nothing -> False
Just d -> ...
Maybe I had the wrong use case for Rust, or I was too stupid to figure things out. Either way, I'm a little happier having decided to literally learn Haskell instead, which turned out to be easier than dealing with Rust, a language which is supposed to be more like languages I was already familiar with (C/C++/imperative).
- steveklabnik 8y agoRust very much supports reading files as iterators, there are even multiple ways to iterate. By line, by byte... Your HashMap issues may have been solved by the Entry API.
- pjmlp 8y agoRust is better suited for use cases where having a GC around is a no-go like OS drivers doing DMA like stuff and high integrity systems, for anything else it is more productive to enjoy having a GC around.