News for hackers. Stuff that matters.
Re:My ideal Rust workflow by capableweb on Wednesday October 27, 2021 @06:24AM (#29011367)
This seems to be about "deploy-flow" or something, rather than the actual workflow, which currently is kind of horrible in Rust thanks to slow compile times and no availability of a REPL.

I've mostly written lisp in my career, with some dips into PHP, JavaScript and other various languages when needed, but mostly interactive lisp environments so far (and lately, mostly Clojure).

How do people develop in Rust? I'm trying to learn it, but it's hard to jump into code-bases and understand the code as I cannot run snippets. Lots of copy and pasting into new "main" files is being done in order to run small snippets of code, to see what they do.

Another way is to isolate parts into smaller test cases, but now I have two pieces of code in two places, one in the actual program and one in the test case. And still I cannot just select code to evaluate when I need to dig deeper into the statements.

Difficulty to evaluate snippets of code together with the slow compile times makes it really hard (for someone used to lisps) to learn and understand Rust.

How does your workflow (when writing/reading code) actually look day-to-day? If you're writing a web server, are you really stopping the server, compiling again, run the full server when doing changes the logic for the server? That seems absolutely bananas and I'm sure I'm missing something vital for my own environment.

Re:My ideal Rust workflow by ekidd on Wednesday October 27, 2021 @09:16AM (#29012883)
> How do people develop in Rust? I'm trying to learn it, but it's hard to jump into code-bases and understand the code as I cannot run snippets.

I might be able to help answer this! I've spent over 10 years of my career writing production code in Lisp or Scheme, and about 5 years now writing occasional production code in Rust. So maybe I can explain how the two workflows differ.

In Lisp, it's super-easy to redefine a function or test some code. You can constantly test small things as you work. And you can easily open a listener on errors and inspect the current state. It's genuinely great.

In Rust, you rely much more heavily on types and tests. You begin by nailing down a nice, clean set of data structures that represent your problem. This often involves heavy use of "enum" to represent various possible cases.

Once you know what your data structures look like, you start writing code. If you're using "rust-analyzer", you'll see errors marked as you type (and things will autocomplete). If you want to verify that something works, you create a function marked "#[test]", and fill it with exactly the same code you'd type into a listener. Maybe you run "cargo watch -x test" in the background to re-run unit tests on save.

Then, maybe 2 hours later, you'll actually run your program. Between rust-analyzer and the unit tests, everything will probably work on the first or second try. If not, you write more "#[test]" functions to narrow down the problem. If that still fails, you can start throwing in "trace!", or fire up a C debugger.

This workflow is really common in functional languages. GHC Haskell has a lovely listener, for example, but I rarely use it to actually run code. Mostly I use it to look up types. The difference is that in strongly-typed languages, especially functional ones, types get you very close to finished code. And some unit tests or QuickCheck declarations take you almost the rest of the way. You don't need to run much code, because the default assumption is that once code compiles, it generally works. And tests are basically just a written record of what you'd type in a listener.

For understanding code, the trick is to look at the data structures and the type signatures on the functions. That will tell you most of what you want to know in Rust, and even more in Haskell.

So that's why I don't particularly miss a listener when working in Rust. Does this answer your question?

Re:My ideal Rust workflow by jxcl on Wednesday October 27, 2021 @10:52AM (#29014070)
Does “listener” mean the same thing as REPL here? I’ve never that term in this context before.
Re:My ideal Rust workflow by lispm on Wednesday October 27, 2021 @03:51PM (#29018253)
One might think that a Listener is a particular user interface for a read-eval-print-loop. The REPL reads s-expressions, evaluates them and prints the resulting s-expresion(s). The Listener might be a window (or a terminal session), which allows typing, editing, commands, completion, help, etc. and which runs a REPL.
Re:My ideal Rust workflow by ekidd on Wednesday October 27, 2021 @11:01AM (#29014192)
Yes. I think MCL (aka Macintosh Common Lisp, an absolutely delightful environment from the 90s) and Allegro Common Lisp (the best Windows Lisp of that period) both called the REPL window "Listener".
Re:My ideal Rust workflow by Zababa on Wednesday October 27, 2021 @09:53AM (#29013291)
> This workflow is really common in functional languages.

That depends on the language. OCaml compiles really fast, has a relatively good toplevel with good tracing, and a great time travelling debugger.

Re:My ideal Rust workflow by simias on Wednesday October 27, 2021 @07:33AM (#29011804)
I can see the frustration coming from Lisp, but it's just business as usual in compiled, strongly typed system languages. Languages like Rust and C++ add insult to injury by having rather long compile times.

It's definitely a different culture but for someone like me who's on the other side of the fence it's not a big deal at all. I lean heavily onto the type checking to make sure that my code is correct and find myself running code relatively infrequently because of that. I can literally code for hours without running any code, just running "cargo check" here and there to check my syntax and typing.

If I really want to evaluate a snippet to make sure that it does what I want I generally just add a #[test] function wherever I'm currently coding. This way I have access to the full environment (dependencies etc...) and I can run it on its own with "cargo test $the_test". And I may get a bonus unit test out of it. So basically TDD without the religion surrounding it.

Re:My ideal Rust workflow by yakubin on Wednesday October 27, 2021 @09:13AM (#29012860)
> I can see the frustration coming from Lisp, but it's just business as usual in compiled, strongly typed system languages.

Not really. SML and OCaml let you evaluate selected code snippets in REPL just fine, and their compilation is fast. I'm not sure about Haskell, but I'd guess it's similar with ghci.

"Strongly-typed" is in the eye of the beholder, but the phrase "compiled, strongly-typed programming languages" generally doesn't bring to my mind C++ and Java of which this statement would be more accurate.

Re:My ideal Rust workflow by snicker7 on Wednesday October 27, 2021 @09:37AM (#29013131)
People confuse strong typing with static typing all the time. I guess this was the case here.
Re:My ideal Rust workflow by simias on Wednesday October 27, 2021 @10:48AM (#29014004)
I did do that, I feel ashamed.
Re:My ideal Rust workflow by yakubin on Wednesday October 27, 2021 @05:06PM (#29019144)
That's a good one. Brought a smile on my face.

From "What We Do In The Shadows": <https://youtu.be/yy4CN9DVPII?t=85>

Re:My ideal Rust workflow by MrBuddyCasino on Wednesday October 27, 2021 @11:50AM (#29014919)
Java IDEs let you evaluate random code fragments at runtime in the context of the current debugger stackframe. You can also change method bodies (not signatures) and have them hot-reload.

It might look like C++, but the JVM is really more like Smalltalk in nature.

Re:My ideal Rust workflow by Zababa on Wednesday October 27, 2021 @09:54AM (#29013304)
Haskell is relatively slow to compile when compared to OCaml.
Re:My ideal Rust workflow by dkryptr on Wednesday October 27, 2021 @10:27AM (#29013720)
I've developed multiple microservices in Rust (using axum[0]) and cargo-watch[1] has really come in handy. I usually only have to wait ~5 seconds for my changes to re-compile and the server to spin up.

I can't really speak for massive projects, but in my experience after the first build the compile times aren't too bad. Only the changed code re-compiles.

Another commenter mentioned they can code for hours without needing to re-compile and I've found that to be true also. I use rust-analyzer[2] in VSCode and I can usually get a lot done without needing to manually test every change. I guess it depends on if I'm writing logic heavy code or writing code-architecture (custom traits, implementations, and whatnot)

[0] - https://github.com/tokio-rs/axum [1] - https://github.com/watchexec/cargo-watch [2] - https://github.com/rust-analyzer/rust-analyzer

Re:My ideal Rust workflow by gameswithgo on Wednesday October 27, 2021 @07:45AM (#29011912)
It is just something you adapt to, in the same way I currently have no idea how people handle tricky refactors in interpreted languages with no compiler assist, but I assume you have developed strategies to do so.

Having a template project for experimentation with bits of code is one way and some simple scripts could automate away most of the pain

Re:My ideal Rust workflow by devit on Wednesday October 27, 2021 @08:25AM (#29012297)
Generally there is no need to actually run the program frequently, so you just code for an hour or more and then run the result.

Since Rust has an actual type system, decent programmers usually produce code that works correctly the first time it successfully compiles, so running and testing it is not so important.

If you still need to run many code snippets frequently, create a command line tool with an option parser (e.g. clap in Rust), and create a subcommand for each "code snippet" and you can then invoke them from the command line along with whatever options you need.

You can also use debuggers or add eprintln! calls in existing codebases.

If you are just learning the language and want to test simple examples you can also use online playgrounds like https://play.rust-lang.org

Re:My ideal Rust workflow by taneq on Wednesday October 27, 2021 @11:20AM (#29014486)
> decent programmers usually produce code that works correctly the first time it successfully compiles, so running and testing it is not so important.

Wow, that is a very big call.

Re:My ideal Rust workflow by skohan on Wednesday October 27, 2021 @08:26AM (#29012313)
I find that rust playgrounds and inline tests are a good solution for trying out snippets. With rust-analyzer, you can run a test via an LSP code-action from right inside the file you're working on, so it's a nice lightweight way to try out a couple lines of code.
Re:My ideal Rust workflow by CraigJPerry on Wednesday October 27, 2021 @06:33AM (#29011410)
His hot reloading rust article is one of my favourites https://fasterthanli.me/articles/so-you-want-to-live-reload-...

This chap is up there with Eli Benderski’s blog for me, i really enjoy fasterthanlime’s long form articles.

Re:My ideal Rust workflow by capableweb on Wednesday October 27, 2021 @06:38AM (#29011447)
Amazing article! Will save for later reading. I jumped straight to the video to get an overview how it's working, and it seems to reload the contents of the file when it changes, which is cool and surely an improvement. Do people use this when they write web servers? Was trying to find if there is any lib/tool you can include/run to get the functionality, but seems very involved right now.

Seems to be missing the vital functionality of being able to evaluate snippets though, which is the biggest feature missing in Rust.

Re:My ideal Rust workflow by ensiferum on Wednesday October 27, 2021 @06:43AM (#29011478)
How exactly do you evaluate snippets in some environment even with REPL? I mean most code in a large system is part of a context that is required at the very least to generate the inputs for your functions. Can't imagine how you could evaluate the these snippets outside of this program environment except in the most trivial cases when dealing with some simple pure functions with only few simple parameters.
Re:My ideal Rust workflow by capableweb on Wednesday October 27, 2021 @06:56AM (#29011551)
Using Clojure as an example (with no regard to quality, not production code and so on). You might have a piece of code like this:

    (defn people-in-scenes [scenes]
      (->> scenes
           (map :subject)
           (interpose ", ")
           (reduce str)))
You could evaluate the full function and then afterwards you evaluate `(people-in-scenes [{:subject "Test"}])`

But, usually you want to evaluate just a piece, in this case we want to evaluate `(->> scenes ...)` but `scenes` is not in scope?

So we evaluate `(def scenes [{:subject "Test"}])` first and now we have scenes in scope wherever we are, with our test data, and we can select arbitrary snippets around the codebase that depend on `scenes` being in scope.

Repeat for all vars that are expected to be in scope, and you can jump around freely.

Sometimes I leave a statement that defines common data-structures to various names within a `comment` block so it's easy to jumpstart this interactive development when you're in a namespace.

There are other ways as well, but normally this is the easiest to get started with and does the trick for most cases.

Re:My ideal Rust workflow by simion314 on Wednesday October 27, 2021 @07:17AM (#29011699)
(I am not OP and I never done lisp.)

>How exactly do you evaluate snippets in some environment even with REPL? I mean most code in a large system is part of a context that is required

This is a bad smell for me. You should always try to split your code in small pure functions that do one thing, it will help you with readability, with refactoring, with testing and depending on environment you could just replace the function with an different one at runtime.

Re:My ideal Rust workflow by Lvl999Noob on Wednesday October 27, 2021 @12:25PM (#29015454)
But you still need the types that are required and you need to define the functions that your snippet calls. Those in turn could require their own types and functions and so on. Unless you don't have much in the name of types and so you can easily define whatever you need. That still leaves the functions that snippet depends on.
Re:My ideal Rust workflow by simion314 on Wednesday October 27, 2021 @12:52PM (#29015832)
I try to create new types if needed instead of passing to a function too many parameters. Also say a function needs the user email you don't pass it the big User object you pass only the email.

The issue I hit though is with code that evolves over years, it starts simple and then each team member had to add some new feature and the simple functions get larger and alrger , the number of parameters increases etc , the solution is to take the time and re-analyze the updated problem and refactor or rewrite the solution to be again composed from simple parts that do one thing.

let me know If I misunderstand the thing you said about types, or you referring mainly about REPL

Re:My ideal Rust workflow by CraigJPerry on Wednesday October 27, 2021 @07:06AM (#29011614)
I don't know how close you could get in rust to the kind of productivity lisp development has.

You would need the ability to keep your program running while inside a debugger while you were able to patch memory on the fly (not just replace instructions in a function body - which I reckon I could figure out with the help of that hot reloading in rust link I shared) but replace function signatures at debug-runtime and be able to serialise the image state to disk and arbitrarily resume it later... It just feels like square peg round hole.

Rust has a different development experience than lisp. It doesn't lend itself to stuff like structural editing (which is really fun to watch an experienced dev doing - Emacs lisp videos on yt are a good source for some mind bending productivity)

Re:My ideal Rust workflow by zozbot234 on Wednesday October 27, 2021 @06:53AM (#29011538)
You do need custom support to do this in Rust, but it's in the works - see https://github.com/google/evcxr for what seems to be the most current effort.
Re:My ideal Rust workflow by capableweb on Wednesday October 27, 2021 @06:58AM (#29011560)
Wow, that looks really cool! Thanks for sharing, gonna give it a try.

Although, the repository seems a bit... Weird. The README ends with:

> This is not an officially supported Google product. It's released by Google only because the (original) author happens to work there.

Why would Google release it if it's not their product/project? Why is it under github.com/google? Do Google own projects that people happen to work on in the free time just because you otherwise work at Google? Something smells fishy here, but wouldn't be the first time Google does fishy stuff.

Re:My ideal Rust workflow by mkl on Wednesday October 27, 2021 @07:37AM (#29011843)
Google releases lots of projects like this, and yes many software developer contracts hand over all IP: https://news.ycombinator.com/item?id=2208056 (discussion is good but original link is dead).
Re:My ideal Rust workflow by emteycz on Wednesday October 27, 2021 @07:45AM (#29011914)
Google is the legal copyright holder, but that does not mean they want to or have to consider it a product.
Re:My ideal Rust workflow by capableweb on Wednesday October 27, 2021 @07:56AM (#29012006)
Holy fuck, I had no idea. How are developers still working at Google if this is true? Giving up ownership of all projects you do in your free-time?
Re:My ideal Rust workflow by follower on Wednesday October 27, 2021 @09:12AM (#29012849)
My understanding (based on second-hand knowledge) is that employees have two "choices" available to them:

* Have a "fairly flexible arrangement" to work on side projects when they want & potentially using Google hardware on work time but in exchange Google holds the copyright & it has to be published under the Google repo with the quoted disclaimer.

* Deal with a bunch more paperwork on per-project basis.

If people don't have a particularly strong attachment to their side-projects the first option is the "low hassle" option for flexibility to do side projects at work.

I would agree it is unfortunate developers don't push back harder against such a wanton "IP" grab by their employer--but hey, I guess that's what we get without collective representation or respecting our own value as developers.

Re:My ideal Rust workflow by tialaramex on Wednesday October 27, 2021 @07:53AM (#29011972)
> If you're writing a web server, are you really stopping the server, compiling again, run the full server when doing changes the logic for the server?

Yes. This is definitely how I'd change the server. I wrote a server that reflected our RDF storage engine as an HTTP SPARQL endpoint. I did that in C, today I would probably use Rust. I'd expect to maybe restart the server say a dozen times in a day's work, maybe less, occasionally more.

Stronger type checking and other pre-compilation inspection might help you? You can have tooling that will show you that what you've written is nonsense and won't compile in a typical IDE like VSCode or even vim.

I don't like REPLs for this type of work, whereas they're definitely what I want for say, live coding music performance or ad hoc database queries.

Re:My ideal Rust workflow by rustnote on Wednesday October 27, 2021 @12:22PM (#29015412)
You might like this extension for VS Code: https://marketplace.visualstudio.com/items?itemName=rustnote...

I just got it to a usable state today, does what you're asking which is run rust code without a main function. Just open a .md file and you're good to go. Also saves the outputs to markdown so you can upload to blog sites. I've only tried it on Linux btw might not work on other platforms.

Re:My ideal Rust workflow by ianpurton on Wednesday October 27, 2021 @08:05AM (#29012100)
> How does your workflow (when writing/reading code) actually look day-to-day? If you're writing a web server, are you really stopping the server, compiling again, run the full server when doing changes the logic for the server?

You can run cargo watch which will see file changes and then stop the server, compile and then re-start it for you.

For incremental changes you're looking at about 2-10 seconds of delay depending on the complexity of the framework in use. i.e. Axum is faster than Actix.

Before you get to that state you're probably in vscode with rust analyzer, this will point out any compiler errors earlier in the lifecycle.

The rust compiler does a lot. It's catching issues before they get to production.

I'm hoping that maybe the m1 pro/max chips can get my incremental compilations to sub 1 seconds and then I get a fast workflow with the full rust compiler safety net.

Re:My ideal Rust workflow by yawn on Wednesday October 27, 2021 @09:03AM (#29012770)
> are you really stopping the server, compiling again, run the full server when doing changes the logic for the server?

Yes. I use something like `cargo watch -x run -w templates` for my personal project. If I make a change to any source file, this command will recompile my project, then restart the web framework's (Tide currently) http server. It also watches changes to the templates (Tera, which is like Jinja2). It's not nearly as fast as some of the dynamic language reloading I've used in other languages/frameworks, but it works and I can move pretty quickly this way.

Re:My ideal Rust workflow by yakubin on Wednesday October 27, 2021 @09:16AM (#29012891)
I feel the pain of a lack of REPL. I frequently want to check a snippet of code, but need to go through the cycle of creating a test case, copying code, modifying it so that it works standalone without its context, running it and then deleting it. It's tiresome.

As for compilation, yes, it's slow in general, but once you compile a project initially, later incremental builds are usually quick.

Overall, I think in the future I'll see if I can use SML more and see how it goes.

Re:My ideal Rust workflow by justsomeuser on Wednesday October 27, 2021 @09:32AM (#29013067)
This is how I keep feedback loops short:

- IntelliJ tells me any type issues instantly (I find this makes up some of the time for not having a REPL).

- Small tests kind of work like a REPL.

- watch.sh running in a terminal, re-compiles and runs the code I am working on. In your web server example, I would be testing the units rather than the whole composite program. I can click file references to jump to the error.

- Fast machine (M1)

Re:My ideal Rust workflow by ianpurton on Wednesday October 27, 2021 @09:50AM (#29013246)
I was thinking of getting an M1 just for this.

Do you know how much faster it is? i.e. make a small change in code to compiled binary.

As most articles for rust compilation focus on a full build which I don't need to do very often.

Re:My ideal Rust workflow by justsomeuser on Wednesday October 27, 2021 @10:46AM (#29013978)
I upgraded from a 2014 MacBook Pro (dual core), it is at least 2x faster for both full and incremental Rust builds.
Re:My ideal Rust workflow by Zababa on Wednesday October 27, 2021 @09:51AM (#29013254)
Reading the docs and the types help me a lot. If you've done Clojure/PHP/JS, maybe you're not used to it
Re:My ideal Rust workflow by duped on Wednesday October 27, 2021 @01:37PM (#29016438)
Like C or C++ you attach a debugger and step through the code
Re:My ideal Rust workflow by bstpierre on Wednesday October 27, 2021 @08:28AM (#29012341)
I'm coming from the opposite side -- my career has had me working almost entirely in compiled languages, mostly c but I've also done very low level embedded assembly and a fair bit of python. For personal projects recently it's been go but have also done some rust, haskell, and have played with some lisps.

I think the thing you're really talking about is getting fast feedback on a bit of code.

The two basic strategies I've adopted are (1) writing a very small, isolated, standalone program or (2) leaning on a test runner.

I use (1) when the snippet is a basic standalone piece of code. Either something that just uses fundamentals from the language, or in an environment where I can generate an executable quickly that is linked to the right libs. Then I can run it and see the output, often iteratively like `ls *.c | entr 'make && ./my-example'`. Sometimes this is also a convenient way to either step through a code snippet in gdb, or examine assembly output.

I use (2) when there are too many dependencies to make an isolated program feasible. I set up a test case to exercise my snippet, and either use a test runner's "watch" functionality or use entr like above, making sure to have the test selection set to only run my test (to keep the feedback loop tight). Then in one terminal I'm editing, and whenever I save I can see output in another terminal. The trick here is to avoid -- as much as possible -- rebuilding the whole system. If the thing you're working on is deep in a library, just build & run the tests for that library. Sometimes you still get burned by touching some file that requires recompiling a whole mess of stuff, but most of the time in my experience with a little bit of effort you can get the feedback cycle down to a small number of seconds.

> How do people develop in Rust? I'm trying to learn it, but it's hard to jump into code-bases and understand the code as I cannot run snippets.

Interestingly I had a VERY similar reaction when trying to learn lisp. I think it's a beginner vs expert kind of mentality. You have a certain mindset to be able to work with lisp, and don't even consciously think about all the little habits you have for working with it. I have a certain mindset to work with compiled static languages. One thing I struggled with in lisp was trying to keep track of what was exactly the state of my repl, given that I had experimented with a whole bunch of things. I found myself frequently having to exit and reload the repl, reload files, etc to get back to a known state, and then occasionally having to scroll back through my history because some critical piece of code that I entered directly in the repl wasn't saved in my source file -- or worse, some definition that was set in a previous revision of a source file (and subsequently removed) was needed so when restarting the system it was missing. And then it's a debugging exercise to try to figure out what critical thing is missing. I've watched people do dynamic development with lisp and it looks really cool but I have no idea how you keep track of what lives where! In my mind, it's a pure, clean slate starting with source files. (Except for environment variables, and the file system, and databases, and maybe the network, and ...)