How to annotate lifetime of `Arc::new` result?

I'm exercising with Arc and trying to make zero-copy structures. And here's a problem I don't know what to do about. Here's the playground with the code.

pub struct OsmElementAttrs<'a> {
    // ...
    pub user: Option<Arc<&'a str>>,

...
pub type ParsedAttrs<'a> = HashMap<&'a str, &'a str>;
impl<'a> TryFrom<ParsedAttrs<'a>> for OsmElementAttrs<'a> {
	fn try_from(attrs: ParsedAttrs) -> Result<Self, Self::Error> {
            ...
			user: match attrs.get("user") {
				Some(v) => Some(Arc::new(v)),
				None => None
			},
    

Error message is:

20 | impl<'a> TryFrom<ParsedAttrs<'a>> for OsmElementAttrs<'a> {
   |      -- lifetime `'a` defined here
21 |     type Error = ParseError;
22 |     fn try_from(attrs: ParsedAttrs) -> Result<Self, Self::Error> {
   |                 ----- has type `HashMap<&'1 str, &'1 str>`
...
32 |                 Some(v) => Some(Arc::new(v)),
   |                            ^^^^^^^^^^^^^^^^^ this usage requires that `'1` must outlive `'a`

How can I make sure the output outlives 'a if I can't annotate lifetimes inline?

What I was trying to do was to implement structs like in osmio crate (arc_types.rc module). There's an example here, but couldn't figure out what I'm missing. I've also tried to add 'b lifetime to output type, but it was just pure guessing and didn't work in any way.

Define try_from like this (added an <‘a>):

fn try_from(attrs: ParsedAttrs<'a>) -> Result<Self, Self::Error> {

(I would’ve expected a complaint about the method definition not matching the trait, but that might come from a different stage of the compiler)

1 Like

Why are you putting a reference &'a str inside an Arc? That will almost always be less efficient and useful than either storing the reference directly or storing an Arc<str> (depending on what your goal is).

4 Likes

It did compile! Thanks!

Oh. Now I see, I did read an article about Arc and it says this is double referencing and double work when reading.

The problem is if I try to remove &'a str the compiler says it can't know str size at compile time:

   Compiling playground v0.0.1 (/playground)
error[E0277]: the size for values of type `str` cannot be known at compilation time
  --> src/lib.rs:32:30
   |
32 |                 Some(v) => Some(Arc::new(**v)),
   |                                 -------- ^^^ doesn't have a size known at compile-time
   |                                 |
   |                                 required by a bound introduced by this call
   |
   = help: the trait `Sized` is not implemented for `str`
note: required by a bound in `Arc::<T>::new`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error

Updated playground.

You can't clone dynamically-sized values by-value. If you only have a &str and you want an Arc<str>, then use the library-provided From impl for cloning the contents of the string.

1 Like

In order to convert a &str to an Arc<str> you must convert it with .into() or Arc::from() — you can't dereference and use Arc::new(), specifically because that can only work with statically-sized (Sized) types.

However, this will copy the characters of the string. In order to get the most benefit from Arc<str> you should create it as early as possible in your program — for example, if you're parsing an input file, do it while parsing, right when you know which substrings you're splitting the input into. This will minimize the number of string copies rather than refcount increments. It's cheap to get an &str from Arc<str> for temporary use, but not vice versa.

On the other hand, if you want a truly zero-copy (as you mentioned in your original post) program, then you cannot use Arc and you must use only &strs that borrow from the original input string. This is often impossible — don't take it as the “correct” way to do things, but as one extreme.

3 Likes

That makes sense. I'll try to switch the HashMap to Arc.

I got back to where I insert-ed into HashMap, and now I see Arc::new(str) caused compiler error, and that's why I worked around with HashMap<&'a str, &'a str>. But Arc::from like in osmio arc_types.rc, does not have this problem.

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.