Recently, I have been learning a new programming language. I know, if you follow this really non-existant blog you would know that this is something that I do often. This time it is Rust. If you have not heard of Rust, it is a system programming language that would be in a similar space as C, only a much more modern design and is focused on memory safety and concurrency. Most of the time I get into a language and read about it a bit, then do a few hello world examples and move on. Rust on the other hand has stuck with me a little longer and I have been working on a “scratch my own itch” project with it. More on Rust itself in a future post.
Rust has a library system as many modern programming languages do and they call them Crates and are registered at http://crates.io. I have been in the process of authoring a crate called PJLink that is a Rust API to control projectors or displays that understand the PJLink protocol. The API is growing and I have been getting to points in development where old school print line debugging was just very inefficient and wanted to use the debugging features built into Visual Studio Code. Code has very good support for programming Rust thought the integration of the Rust Language Server and debugging using lldb inside of VS Code.
I’m new to Code and only started using it when I started checking out Rust and heard how great it was for beginners because RLS has code completion/docs and is compiling your code all the time and showing your errors before you actually go to run the code. In most cases, if you remove all of the errors that RLS reports your code will compile. Debugging a standard Rust binary was pretty straight forward, but I was having trouble finding out how to do it if I was debugging a library using an example program that is included in the examples directory of the Rust crate. More about example programs and crates here. I don’t need to go into that for this post.
That is a lot of info to get to this short example, but I had looked around a few times on how to do this and couldn’t find anything so I spent more time than I would like to mention trying to figure it out today. Here is what I came up with.
In Rust you can run a example in a library/crate by running:
cargo run --example get_input -- 192.168.1.1 password
Where “get_input” is the name of a Rust file “get_input.rs” in the examples folder of the Rust project folder. The arguments that are after the “–” are the arguments that are passed to the get_input example program. Code uses a launch.json file to define multiple debugging configurations which lives in the .vscode folder in your project director and one is created the first time that you go to run the debugger on a project. Here is an example of how to debug a standard Rust program:
{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "type": "lldb", "request": "launch", "name": "Debug pjlink-cli", "cargo": { "args": [ "build", "--bin=pjlink-cli" ] }, "args": ["192.168.1.1", "INPT ?", "password"], "cwd": "${workspaceFolder}" } ] }
This was generated for me and worked great, but what was generated by Code didn’t give me what I was looking for to debug a crate/library that didn’t have a standard Rust main. I did notice that it was using the Cargo build tool that is part of the very powerful tooling of Rust. So I knew I could get it to work, but it was not as obvious as I thought it would be. Here is what I came up with.
{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "type": "lldb", "request": "launch", "name": "Debug set_input", "cargo": { "args": [ "build", "--example", "get_input" ] }, "args": ["192.168.1.1", "password"], "cwd": "${workspaceFolder}" } ] }
This is really not much different than the example above. The main changes are the addition of the “–example” and “get_input” example file to the build arguments. I think that I just keep getting wrapped up in the way I would “cargo run” and didn’t think to add the same arguments to cargo build. Now that I’m through it, this seems simple and logical, but I’m hoping that someone out there struggles with the simple like I did.