[Ruby to Rust] Clarity with HashMaps

Here's the context for the ruby code:

ec2.keys.sort.each do |type| {
instances=ec2[type]; 
delta = ((instances.length - reservations[type]) rescue 0); 
message += "`#{type}`: *need_reservation=#{delta}* running=#{instances.length} reserved=#{reservations[type]} instances=_#{instances.to_s}_\n\n\n" 
}

context for the rust code I'm trying to write:

ec2.keys().sorted().for_each(|r#type|{
        let instances = &ec2[r#type];
        let delta = instances.len() as u64 - reservations.get(&r#type.as_str()).expect("mismatched types");
        let instances_str: String = instances.iter().map(ToString::to_string).collect();
        message += format!("`{}`: *need_reservation={}* running={} reserved={} instances=_{}_\n\n\n", 
        r#type, delta, instances.len(), reservations[r#type], instances_str);
    });

I have a couple questions. for the line with:

let delta = instances.len() as u64 - reservations.get(&r#type.as_str()).expect("mismatched types");

I was only able to get it to compile as written, and &reservations[r#type] would not work for me the way it worked for ec2 in the first line. I was wondering why this is the case. Also is using ".expect" the equivalent for "rescue 0"?

My main problem, however, is in this formatted string line:

message += format!("`{}`: *need_reservation={}* running={} reserved={} instances=_{}_\n\n\n", 
        r#type, delta, instances.len(), reservations[r#type], instances_str);

The way I understood the format function is that you can put any type into the corresponding "{}". However, I am getting errors with "reservation[#type] saying "the trait Borrow<&std::string::String> is not implemented for &str". I tried replacing it with:

reservations.get(&r#type.as_str())

But that gives the error "std::option::Option<&u64>cannot be formatted with the default formatter". Is there a different format function that would make this work? Finally, the "instances_str" right after reservations also gives me an error saying "expected&str, found struct std::string::String`. That's why I tried

let instances_str: String = instances.iter().map(ToString::to_string).collect();

and

instances.to_string()

But I still keep getting the same error. For reference, instances is a vector and reservations is a HashMap with key of type String and value of type u64.
Any help would be greatly appreciated.

I believe the equivalent code to the delta computation from Ruby would be like this:

let delta = match reservations.get(r#type.as_str()) {
    Some(val) => instances.len() as u64 - val,
    None => 0,
};

Using .expect will cause a panic (your program to crash) if the value is not present.

As for the other error, it would be helpful if you could post the whole compiler error, including the code snippets, as it's tricky to determine what exactly they are referring to from the context you have given.

Somewhere, you are trying to format an Option, which cannot be formatted with {}, but I can't tell which variable is the Option without more context.

1 Like

No you can't. You can only use that to format types implementing the Display trait. Please read the documentation.

The entire compiler error:

error[E0277]: `std::option::Option<&u64>` doesn't implement `std::fmt::Display`
   --> src/main.rs:210:135
    |
210 | ...nces.len(), reservations.get(&r#type.as_str()), instances_str);
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::option::Option<&u64>` cannot be formatted with the default formatter
    |
    = help: the trait `std::fmt::Display` is not implemented for `std::option::Option<&u64>`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
    = note: this error originates in the macro `$crate::__export::format_args` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0308]: mismatched types
   --> src/main.rs:210:20
    |
210 | ...+= format!("`{}`: *need_reservation={}* running={} reserved={} instances=_{}_\n\n\n", r#type, delta, instances.len(), reservations.get(&r#type.as_str()), instances_st...
    |       expected `&str`, found struct `std::string::String`
    |
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)

So like @H2CO3 said, reservation.get(&r#type.as_str()) is a u64 that doesn't have a Display trait. What can I do instead to fix that?
Additionally, I think I need to convert instances_str from a String to a &String. How can I accomplish that?

reservation.get(&r#type.as_str()) is an Option<&u64>, and it's the Option, not the u64 that doesn't implement Display. The question here is: what do you want to do if get() can't find the specified key in reservation? Using unwrap() or expect() would make it panic if it can't find the key, which may be the simplest solution if you expect the key to always be present. You could instead provide a default value if the key can't be found using unwrap_or(). There are also other options such as propagating an error up the call stack or giving some special handling by matching on the Option.

2 Likes

The second error is about appending to the string; the format! macro returns a String, but String += expects a &str.

You should be able to fix it by doing format!(...).as_str(), or you could do write!(&mut message, "...{}...", ...); instead of format!.

3 Likes

I wish I could give 2 people solution, but this worked for me, thanks!