I'm not a novice programmer, but I am bit lost when trying to do my first debugging in Rust with RustRover. I suspect it might have something to do with how things are stored in collections, but I can't figure out how I can do something "simple" as looking into a serde_json Value I have, to see what it contains. When I try to run functions on it, it gives complaints that certain fields don't exist.
This is the struct and part of the function that I am working with:
struct TidalSingleAlbumResponse {
data: serde_json::Value,
included: Vec<serde_json::Value>,
}
fn to_tidal_album(album: &TidalSingleAlbumResponse) -> Result<TidalAlbum, &str> {
let Some(serde_json::Value::String(id)) = album.data.get("id") else {
return Err("did not find id.");
};
// Title and barcode are in the "attributes" object
let Some(serde_json::Value::Object(attributes)) = album.data.get("attributes") else {
return Err("did not find attributes.");
};
let Some(serde_json::Value::String(title)) = attributes.get("title") else {
return Err("did not find title.");
};
// Get availability and convert it to Vec<String>
let Some(serde_json::Value::Array(availability)) = attributes.get("availability") else {
return Err("did not find availability.");
};
//// <--- SNIPPED CODE HERE --->
What I am trying to do is inspect album.data and see what values it contains. Specifically, I am trying to see why on one JSON object it cannot find the "availability" key, when it is clear there in the response.
Expanding the whole tree on album.data in the debugger tells me nothing useful, and as you can see: trying expressions like album.data.get("id") do not work, while it does work in the function.
Also, something like album.data.as_object().unwrap().keys() gives the following error in the debugger: error: extra tokens at EOF.
So, this is what I tried to inspect the serde_json::Value. How can I actually do something useful with the debugger in this case? Right now it feels like my only resort is to just log what is going on.
I found the settings to change the renderer and the debugger and tried all different combinations. It still yields the same results for me. The of the serde_json Value looks exactly as above.
I tried this as well now, but it gives the same results.
I still have the idea that it is something about the serde_json Value structs that make it hard to inspect. As in: the data is there, but I just don't know how to look at it.
Also, I think I can't execute implemented methods on variables in the debugger? Is this something that is just not possible? I'm used to doing this in Python, but I suppose it easier for a non-compiled language to just execute methods on it?
Example, based on the screenshots above:
This does not work:
serde_json::Value::as_object(&album.data)
--> error: could not find decl "as_object"
Neither does this, because it tries to look for the field called as_object rather than executing the method on it.
album.data.as_object()
--> error: no field named as_object
Perhaps you're looking at the wrong place. Here's what I get under root/0/node/pointer for the first bunch of data on a simple test with a BTreeMap<&str, i32> which has 3 values (here, "data" => 30):
EDIT: see also next post about how it looks when using GDB instead of LLDB.
BTreeMaps aren't the easiest to visualize; have you tried asking JetBrains about your problem? They're usually helpful, and I suppose they're adding types progressively. For instance, enum is now much easier to visualize, even on Windows (what OS are you using?).
I see that HashMap is easier to visualize than BTreeMap, but you probably won't be able to change what serde_json is using (I don't know the crate enough to tell).
To be frank, I'm rarely using a debugger with Rust because they're not mature yet (though it's getting better); instead, I'm only relying on logs. What you could do is something like this and inspect the string:
if let Some(debug_value) = album.data.get("id") {
let debug_str = format!("{debug_value:?}");
let x = 0; // put the breakpoint here
}
Or you could transfer a series of key/value pairs to another type and inspect that instead.
You could also try VSCodium, which uses another version of LLDB, but the experience isn't really the same.
Where are you trying this? In LLDB, or in the watchlist? Yes, the debugger has very little notions of Rust, and calling methods would imply calling the corresponding code in your program, which may not exist (not even mentioning the debugger being able to understand the whole structure of your code). It's definitely not as flexible as debuggers for reflective languages like Kotlin, C# or Java. You can read more about debugging support here.
On another note, I've quickly checked by using the GNU toolchain, and this is much better (as usual):
I'll give that one a try, but it depends on the availability of a nightly feature.
I'm using things like try_collect() everywhere.
EDIT: I tried adding the expressions above in the watchlist, yes. I have never typed things manually into a debugger. It's a skill I didn't have to learn yet.
I'm not sure. In the Run/Debug configuration, I tried to:
add "+stable-x86_64-pc-windows-gnu" before "run [...]" in the cargo command, but RustRover moves it after "run", so it doesn't work
specify the RUSTUP_TOOLCHAIN environment variable, but it didn't work
You can run an external tool (command) before launching the tests, which might work. Perhaps I overlooked an easier way to do that; for example, with the rust-toolchain.toml configuration? I see there's a feature request to put alternate configurations, but it's still open.
At least, it's not necessary to switch the toolchain on Linux: you just have to switch the debugger in the config. I'm not even sure there's another than x86_64-unknown-linux-gnu.
The GNU toolchain is slower when compiling projects and performing doc tests, so I switch when I absolutely need to use the debugger, then I switch back. It's not hard to script those commands, or even to type manually, but logs are usually easier to use with complicated data structures anyway.
Oh yes, indeed! Getting there, slowly...
The same toolchain versions are available in msvc and gnu, so you shouldn't see any difference. I'm never using unstable versions except when I test with Miri, so I can't tell more than that.