Without the Arc, why following code can still work?

Is that because the str somehow same as Arc does, or the lifetime influence the mem layout which makes the name still alive after move.

use std::thread::spawn;

#[derive(Debug)]
struct User<'a> {
    name: &'a str,
}

fn main() {
    let user = User { name: "drogus" };
    

    let t1 = spawn( move || {
        println!("1st thread -> {:?}", user.name);
    });
    
    let t2 = spawn( move || {
        println!("2nd thread -> {:?}", user.name);
    });

    t1.join().unwrap();
    t2.join().unwrap();
}

(Playground)

Output:

1st thread -> "drogus"
2nd thread -> "drogus"

Errors:

   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 0.54s
     Running `target/debug/playground`

The user.name field is moved into each thread. Since &'static str is Copy that is ok.

Note that "drogus" compiles down to a reference into an immutable global that holds the actual bytes. The immutable global lives forever, so it's okay to access it from other threads.

3 Likes

I understand, but how to handle when the member is String, following your explaination:

  1. Copy, no String is not Copy. How to make it Copy
  2. lifetime, Can String be given static lifetime?

For 1, is that possible to modify an existed struct, like String to become Copy

String is not Copy. Once you move it, you can no longer use the original.

In that case, you can clone it as necessary.

String meets the 'static bound (String: 'static), because it owns its data; it doesn't contain any non-'static references for example. You may have some of these misconceptions about what lifetime bounds on types mean. (The Strings still get destructed in my example, for instance.)

2 Likes

Is that possible to add Copy to existed Struct, for example String, is that allowed in Rust?

Normally, a Struct implement Copy will meet something like:

#[derive(Debug, Clone, Copy)]
struct User<'a> {
    name: &'a str,
}

How to make a Struct from:

#[derive(Debug)]
struct User<'a> {
    name: &'a str,
}

to

#[derive(Debug, Clone, Copy)]
struct User<'a> {
    name: &'a str,
}

without touch the source code :hugs:

The Copy trait can only be derived on a user-defined struct if all of its fields implement it as well.

And no, you can't add any new code to an existing code without touching it. That's a logical contradiction.

Here's the documentation.

Rust doesn't have copy constructors. Copying and moves in Rust are always equivalent to a memcpy (just copying the shallow bytes around). Moves invalidate the place that was moved from to avoid things like the double-free I mentioned. Copies do not.

So you can only add Copy to structs that meet certain requirements, and String doesn't meet those requirements. String can't be Copy as that would result in a double-free. (Rust has no language level garbage collection and Strings are deallocated when dropped.) You can only implement Copy for types whose fields are also Copy. They can't involve Drop types or exclusive references or anything else that would cause memory problems, etc.


If a foreign type doesn't implement Copy, you can't make it Copy and can't make any local type which owns one Copy either.

So if there is a

#[derive(Debug)]
struct User<'a> {
    name: &'a str,
}

in someone else's code, there's no way you can make it behave like

#[derive(Debug, Clone, Copy)]
struct User<'a> {
    name: &'a str,
}

But perhaps you could use a &User<'a> or such instead.

3 Likes

I am not sure, if it is called Reflection to describe such behavior;

Im describing to make the it the same in functionality, not really "add any new code to an existing code without touching it".

Besides, I see David Tolnay who write syn and quote has a repo called reflect.

Is that could be used sovled this simple question?

:hugs: checked the source code for String, the related part we discussed is pasted as follows:

For String;


#[derive(PartialEq, PartialOrd, Eq, Ord)]
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), lang = "String")]
pub struct String {
    vec: Vec<u8>,
}

#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
impl Clone for String {
    fn clone(&self) -> Self {
        String { vec: self.vec.clone() }
    }

    fn clone_from(&mut self, source: &Self) {
        self.vec.clone_from(&source.vec);
    }
}

As the field of String, Vec;


#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "Vec")]
#[rustc_insignificant_dtor]
pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
    buf: RawVec<T, A>,
    len: usize,
}

#[cfg(not(no_global_oom_handling))]
#[stable(feature = "extend_ref", since = "1.2.0")]
impl<'a, T: Copy + 'a, A: Allocator> Extend<&'a T> for Vec<T, A> {
    fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
        self.spec_extend(iter.into_iter())
    }

    #[inline]
    fn extend_one(&mut self, &item: &'a T) {
        self.push(item);
    }

    #[inline]
    fn extend_reserve(&mut self, additional: usize) {
        self.reserve(additional);
    }
}

It's clear that usize has Copy, Unique has Copy thus, RawVec could has Copy, oh...it's a long chain for such purpose...

Let's simply use the Arc for it :hugs:

1 Like

Vec implements Drop and thus cannot be Copy, no matter what its fields are. (RawVec too.)

Moreover, all those private fields and structs are implementation details. Hacking something together to get something with added guarantees (like a trait implementation) which an upstream library hasn't provided you themselves would be wildly unsound. In this case, "no way to make use of it without UB" levels of unsound; the compiler itself (i.e. the language) depends on the guarantees of the Copy trait.

I.e. there is no solution. The language doesn't allow it.


The Extend block has nothing to do with this conversation; that's about copying data into a Vec, not the Vec itself.

3 Likes

But that would be completely wrong. You are completely ignoring the requirement for managing memory.

When the compiler can allow a type to be Copy, it doesn't mean that it automatically makes sense for it to be Copy. Some types intentionally need to not implement Copy.

Types which have a destructor are one example. If you could copy a vector like that, then you'd have two (or more) instances with the exact same buffer pointer, which both would be trying to free – which is wrong.

But this is just the trivial, obvious case. Some types are designed to represent unique access to a resource otherwise not connected with memory.

So no, neither RawVec nor Vec can be Copy.