I finally got around to writing a teeny Rust program. As ever for me, I needed a itch that I could scratch before I could write something. I’ve dabbled in Rust before, but mostly as build fixes for NetBSD. Unfortunately/stupidly I wrote this at work so I can’t share it in full here, but I can write up my notes much like I did my Golang ones.
- As a rough guide for my brain I like to think of Rust as an imperative version of Haskell, specifically Haskell via Stack…
- …because when creating new things you should use
cargo
to get started. I.e.cargo new my-project
instead of just creating a new file with a*.rs
extension and trying to userustc
. - The error messages when trying to build/compile are very Haskelly - yes, they probably do tell you exactly what is wrong, but in general they aren’t any help (and you will just end up searching) unless you are an expert and then you probably aren’t making those mistakes anyway;
rustc --explain
is cool though. - Need to use the keyword
mut
if you want a mutable variable. - The amount of dependencies scare me a bit. I used reqwest. Just adding that one dependency to my project resulted in about 90 crates being compiled. Maybe there is something lighter-weight than reqwest? Perhaps ureq. Nothing built-in? Also, I had to explicitly enable a blocking/non-async client by setting
reqwest = { version = "0.11", features = ["json", "blocking"] }
in the[dependencies]
section of myCargo.toml
because I didn’t need the complexity of async; Although it seems difficult to actually escape. - A lot of Rust examples are out of date. This can be true for any language, but I guess Rust has evolved rapidly. E.g. I started looking at this example reqwest code, but found out
error_chain
was no more. Box<dyn Error>
is super useful, specifically usingfn main() -> Result<(), Box<dyn Error>>
otherwise you can’t get out of complier errors as you can’t appease all the expected error types.- The
?
operator and error propagation stuff makes me think of Haskell’sData.Maybe
. - Related: the use of
unwrap()
for quick/lazy getting at of values is like Haskell’sfromJust
. In both cases you probably shouldn’t really use it, but it’s fine for quick/little programs. - serde_json works really similar to Haskell’s Aeson. I like this a lot.
-
Especially using the
#[derive(Serialize, Deserialize, Debug)]
stuff:use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] struct NotificationRuleList { total: i32, notification_rules: Vec<NotificationRule>, } #[derive(Serialize, Deserialize, Debug)] struct NotificationRule { id: String, #[serde(rename = "type")] rule_type: String, // Needed to rename this field as `type` is a reserved keyword start_delay_in_minutes: i32, urgency: String, contact_method: ContactMethod, } #[derive(Serialize, Deserialize, Debug)] struct ContactMethod { id: String, #[serde(rename = "type")] method_type: String, // Needed to rename this field as `type` is a reserved keyword } [...] let response = client .get(format!( "https://api.pagerduty.com/users/{pd_userid}/notification_rules?urgency=low" )) .header(AUTHORIZATION, format!("Token token={pd_token}")) .header(ACCEPT, "application/json") .send()? .text()?; let notification_rule_list: NotificationRuleList = serde_json::from_str(&response)?; # Maybe there is probably a way to use `.json()` instead of `.text()` and go straight to `serde_json`? for mut notification_rule in notification_rule_list.notification_rules { [...]
These examples are for parsing the PagerDuty notification rules response.
- Using
Debug
is handy for printing out the structure:println!("deserialized = {:?}", notification_rule_list);
. - I still don’t understand
String
vs&str
. I just know you can’t pattern match on String so have to domatch method_type.as_str()
. - The wiki page is a really good introduction and I should have read that first.
- The book is also very good (already linked to bits of it here).
- Cross compiling doesn’t seem to be as easy as Golang, although this could be because I was trying to go from macOS arm64 to NetBSD amd64:
- Need(?)
rustup
rustup target add x86_64-unknown-netbsd
cargo build --target=x86_64-unknown-netbsd
(rustc --print target-list
is handy as wasn’t sure what it was called, had tried withamd64-unknown-netbsd
)- But then I hit errors with openssl
-
So then I tried
rustls-tls
, but then hit:error occurred: Failed to find tool. Is `x86_64--netbsd-gcc` installed?`
- I gave up
- Need(?)
That’s it for now, hope I manage to find reasons/excuses to do some more.
[EDIT: 2024-02-15] Added some further notes on cross-compilation.