Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to create an Arc<[T]> from a Vec<T>?

Tags:

rust

To be more specific, why doesn't Arc<T> implement from_raw with a dynamically sized T while Box<T> does?

use std::sync::Arc;

fn main() {
    let x = vec![1, 2, 3].into_boxed_slice();
    let y = Box::into_raw(x);
    let z = unsafe { Arc::from_raw(y) }; // ERROR
}

(play)

As pointed out in the comments, Arc::from_raw must be used with a pointer from Arc::into_raw, so the above example doesn't make sense. My original question (Is it possible to create an Arc<[T]> from a Vec<T>) remains: is this possible, and if not, why?

like image 539
John Avatar asked Jun 19 '17 17:06

John


2 Answers

As of Rust 1.21.0, you can do this:

let thing: Arc<[i32]> = vec![1, 2, 3].into();

This was enabled by RFC 1845:

In addition: From<Vec<T>> for Rc<[T]> and From<Box<T: ?Sized>> for Rc<T> will be added.

Identical APIs will also be added for Arc.

Internally, this uses a method called copy_from_slice, so the allocation of the Vec is not reused. For the details why, check out DK.'s answer.

like image 133
Shepmaster Avatar answered Oct 27 '22 17:10

Shepmaster


No.

First of all, as already noted in comments, you can't toss raw pointers around willy-nilly like that. To quote the documentation of Arc::from_raw:

The raw pointer must have been previously returned by a call to a Arc::into_raw.

You absolutely must read the documentation any time you're using an unsafe method.

Secondly, the conversion you want is impossible. Vec<T>Box<[T]> works because, internally, Vec<T> is effectively a (Box<[T]>, usize) pair. So, all the method does is give you access to that internal Box<[T]> pointer [1]. Arc<[T]>, however, is not physically compatible with a Box<[T]>, because it has to contain the reference counts. The thing being pointed to by Arc<T> has a different size and layout to the thing being pointed to by Box<T>.

The only way you could get from Vec<T> to Arc<[T]> would be to reallocate the contents of the vector in a reference-counted allocation... which I'm not aware of any way to do. I don't believe there's any particular reason it couldn't be implemented, it just hasn't [2].

All that said, I believe not being able to use dynamically sized types with Arc::into_raw/Arc::from_raw is a bug. It's certainly possible to get Arcs with dynamically sized types... though only by casting from pointers to fixed-sized types.


[1]: Not quite. Vec<T> doesn't actually have a Box<[T]> inside it, but it has something compatible. It also has to shrink the slice to not contain uninitialised elements.

[2]: Rust does not, on the whole, have good support for allocating dynamically sized things in general. It's possible that part of the reason for this hole in particular is that Box<T> also can't allocate arrays directly, which is possibly because Vec<T> exists, because Vec<T> used to be part of the language itself, and why would you add array allocation to Box when Vec already exists? "Why not have ArcVec<T>, then?" Because you'd never be able to construct one due to shared ownership.

like image 35
DK. Avatar answered Oct 27 '22 19:10

DK.