0

The below code doesn't work because the argument to thread::spawn requires that some is borrowed for 'static, I get it:

fn main() {
    let some = "Some".to_string();
    let apple = Arc::new(&some);

    for _ in 0..10 {
        let apple = apple.clone();

        thread::spawn(move || {
            println!("{:?}", apple);
        });
    }
}

But how come the following works? Since String is not Copy, how come this doesn't also cause the borrowed value to not live long enough? Is the String becoming static to adjust for the thread code?

fn main() {
    let some = "Some".to_string();
    let apple = Arc::new(some);

    for _ in 0..10 {
        let apple = apple.clone();

        thread::spawn(move || {
            println!("{:?}", apple);
        });
    }
}
trent
  • 25,033
  • 7
  • 51
  • 90
sarik123
  • 21
  • 4
  • `String` is `'static`, which is not the same as saying that `some` is static. See [The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want](https://stackoverflow.com/q/40053550/3650362) – trent Jun 20 '21 at 20:37
  • 1
    you're creating an Arc to a reference, and the referenced doesn't live past main. I can't see a use case for using an Arc to a reference, arc is itself a reference to its contained object after all. Generally you want to give ownership of the variable to the Arc ( 2nd snippet ). Thus instead of having the string living just through main, it lives as long as all the clones of the arc lives. – vincent Jun 20 '21 at 22:01
  • 1
    @vincent thanks for the answer.Also for guiding why it's not a good idea to use Arc to a ref – sarik123 Jun 21 '21 at 13:15

2 Answers2

4

Quoting the Arc docs

The type Arc<T> provides shared ownership of a value of type T, allocated in the heap. Invoking clone on Arc produces a new Arc instance, which points to the same allocation on the heap as the source Arc, while increasing a reference count. When the last Arc pointer to a given allocation is destroyed, the value stored in that allocation (often referred to as “inner value”) is also dropped.

Shared references in Rust disallow mutation by default, and Arc is no exception: you cannot generally obtain a mutable reference to something inside an Arc. If you need to mutate through an Arc, use Mutex, RwLock, or one of the Atomic types.

TL;DR: Arc takes ownership of the passed value.

If the value is a reference, obviously only this reference is shared, not the actual data. Because you are moving the Arc (or its clones) to other threads, Rust cannot know how long it will live and therefore requires the contained data to be 'static.

F0X
  • 370
  • 4
  • 13
1

To keep it simple you could think of an Arc as keeping the value it contains alive while it or any of its clones are alive.

In your first example the contained value is the reference of some. But references may only live as long as what they borrow from. Therefor the borrow may only be kept alive by the Arc's for the lifetime of some and this means the Arc's themselves may only be kept alive for the same lifetime as otherwise they would keep the borrow alive for too long. This conflicts with the requirement of std::thread::spawn.

In your second example the contained value is the actual String value of some that is moved into the Arc. Now there is no borrowing constrained as the Arc actually owns the String and can keep it alive as long as it or any of its clones live. Therefor the Arc can be kept alive for the 'static lifetime if necessary as it can in turn keep the String alive. This means std::thread::spawns requirement is fulfilled as the cloned Arc can be kept alive for long enough.

Skgland
  • 862
  • 10
  • 24