Description
The unstable trait PointerLike
must be implemented for any type which is to be coerced to a dyn*
type. However, the only built-in smart pointer type it is currently implemented for is Box
, and this is a special case in the compiler.
I believe it would be useful for PointerLike
to be implemented for, among other things, Rc
, Arc
, and their corresponding Weak
types. This would enable use cases such as polymorphic collections like Vec<dyn* SomeTrait>
where each element consists of a (strong or weak) reference-counted pointer to other data structures, whereas the current available option is Vec<Box<dyn SomeTrait>>
which results in an inefficient extra allocation and double-indirection for each item.
However, it is currently impossible even to modify the standard library to meet this requirement. This is because the compiler requires that the type implementing PointerLike
be either primitive or repr(transparent)
, and types like Rc
cannot be repr(transparent)
because they have an allocator field:
pub struct Rc<
T: ?Sized,
#[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
> {
ptr: NonNull<RcInner<T>>,
phantom: PhantomData<RcInner<T>>,
alloc: A,
}
The only non-zero-sized field of Rc<T>
is ptr
, but Rc<T, SomeNonZstAllocator>
is non-zero-sized, which disqualifies the entire Rc
type from being able to have repr(transparent)
, and therefore means you can't even implement PointerLike
for Rc<T>
= Rc<T, Global>
.
It seems to me that, therefore, it would be useful to adjust the design of PointerLike
’s implementation restriction — or of repr(transparent)
, or something — so that impl PointerLike<T> for Rc<T>
is possible. Box
manages to implement PointerLike
only by way of having been granted a unique “is Box
” exception. This exception could be generalized to other standard library types, and that would address being able to use Rc
and friends in dyn*
, but it would privilege the standard library in a way that has no path to stabilization.
(If dyn*
settles into being solely an async trait implementation detail and not a language feature, then this entire issue is arguably moot since users won't be getting to write Vec<dyn* Trait>
at all — but PointerLike
would still be potentially useful as a trait bound to people trying to solve similar problems in libraries.)
For an example of the kind of code I’m interested in writing:
#![allow(incomplete_features)]
#![feature(pointer_like_trait)]
#![feature(dyn_star)]
use std::cell::Cell;
use std::rc::{Rc, Weak};
pub trait Watcher<T> {
fn notify(&self, value: &T);
}
pub struct Watched<T> {
value: T,
watchers: Vec<dyn* Watcher<T>>,
}
impl<T> Watched<T> {
pub fn set(&mut self, value: T) {
self.value = value;
for watcher in self.watchers.iter() {
watcher.notify(&self.value);
}
}
}
#[repr(transparent)]
struct CopyToCell<T>(Weak<Cell<T>>);
impl<T> core::marker::PointerLike for CopyToCell<T> {}
impl<T: Copy + 'static> Watcher<T> for CopyToCell<T> {
fn notify(&self, value: &T) {
if let Some(cell) = self.0.upgrade() {
cell.set(*value);
}
}
}
fn main() {
let c = Rc::new(Cell::new(0));
let mut w = Watched {
value: 0,
watchers: vec![
CopyToCell(Rc::downgrade(&c))
],
};
w.set(1);
assert_eq!(c.get(), 1);
}
This code does not compile, but the only reason it does not is that the PointerLike
implementation is rejected. You can prove this by wrapping the Weak
in an extra Box
, and, it will compile and run on current nightly.
@rustbot label +F-dyn_star