Returning a fallback without referencing a temporary

Suppose you have a struct Big and a function get_big that fetches a Big from a map, ideally borrowed. If it isn't in the map, get_big should return a fallback value of Big. Values in Big can be large enough that they should not be needlessly cloned, but the fallback value is cheap to construct or clone. My question is about the best way to implement get_big, especially choosing a return type.

The obvious (to me) approach, which returns &Big, fails:

// Question is meant for general Big, but here's an example.
struct ExampleBig { members: Vec<String> }

fn get_big(map: &BTreeMap<i32, ExampleBig>, key: i32) -> &ExampleBig {
  if let Some(found_s) = map.get(&key) {
    found_s
  } else {
    // ERROR: Can't borrow a temp value.
    &ExampleBig { members: vec![] }
  }
}

Here are the alternatives and fixes I could come up with:

  • Return Big, cloning in the found-in-map case, which as mentioned above is not ideal here.
  • Return Option<&Big> and repeat .unwrap_or(&fallback) at every callsite. (Sadly, the fallback is not always the default value.)
  • Return &Big. On the struct with the map, store an owned fallback value in a separate field, and return references to it. (This is what I have been doing.)
  • Return &Big. Lazily create an owned fallback Big value with one of the OnceCell patterns, and return references to it.
    • I don't know a good way to do this if Big is generic, which it is in my case.

In my case, this is just trying to squeeze out some extra convenience/ergonomics from typical map usage, rather than an essential need. Returning Option<&Big> is probably the most idiomatic choice. The question is being asked more out of surprise that I couldn't come up with a nice solution.

I am interested to hear if there's a better way I didn't think of.

Perhaps return a Cow.

Given what you've described, that's what I would do. Choosing the fourth bullet if the fallback Big was expensive.

2 Likes

Is your fallback value always the same? If so, you can just put it in a static and return references to that.

If no, then what about returning a custom enum that contains a &Big or an owned fallback, and implementing appropriate helper methods for it?

3 Likes

If your Big fallback can be const-initialized you can create it in a const {} block and exploit rvalue static promotion to get a &'static reference to it. This works even if Big is generic.

If Big is not generic you have to use some kind of static generic map, but then this could become slower rather than faster.

Otherwise your third option is a pretty good solution too:

2 Likes

Thank you all for the replies. I asked this hoping to learn something and got what I wanted. I forgot Cow existed, and static promotion is new to me. Also useful to hear that storing an owned fallback can't be trivially improved.

1 Like

or you can return Option<&T>, and make the caller worry about it with something like get(…).unwrap_or(&fallback).

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.