Skip to content

Commit eb68b36

Browse files
Rollup merge of #156828 - P8L1:share-trait-core-bootstrap, r=SimonSapin
Add unstable Share trait Tracking issue: #156756 This adds an initial unstable `Share` trait for clone-as-alias types, as part of the 2026 ergonomic ref-counting project goal. ```rust pub trait Share: Clone { fn share(&self) -> Self { Clone::clone(self) } } ``` This PR adds a separate unstable feature gate: ```rust #![feature(share_trait)] ``` and places the trait next to `Clone` in `core::clone`. Implemented initial impls: - `impl<T: ?Sized> Share for &T` - `impl<T: ?Sized, A: Allocator + Clone> Share for Rc<T, A>` - `impl<T: ?Sized, A: Allocator + Clone> Share for Arc<T, A>` - `impl<T> Share for std::sync::mpsc::Sender<T>` - `impl<T> Share for std::sync::mpsc::SyncSender<T>` The PR deliberately does not add `Share` to the prelude. r? @nikomatsakis @rustbot label F-ergonomic_clones
2 parents e938cd8 + 2f953da commit eb68b36

20 files changed

Lines changed: 473 additions & 3 deletions

library/alloc/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@
144144
#![feature(ptr_metadata)]
145145
#![feature(rev_into_inner)]
146146
#![feature(set_ptr_value)]
147+
#![feature(share_trait)]
147148
#![feature(sized_type_properties)]
148149
#![feature(slice_from_ptr_range)]
149150
#![feature(slice_index_methods)]

library/alloc/src/rc.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ use core::any::Any;
245245
use core::cell::{Cell, CloneFromCell};
246246
#[cfg(not(no_global_oom_handling))]
247247
use core::clone::TrivialClone;
248-
use core::clone::{CloneToUninit, UseCloned};
248+
use core::clone::{CloneToUninit, Share, UseCloned};
249249
use core::cmp::Ordering;
250250
use core::hash::{Hash, Hasher};
251251
use core::intrinsics::abort;
@@ -2525,6 +2525,9 @@ impl<T: ?Sized, A: Allocator + Clone> Clone for Rc<T, A> {
25252525
#[unstable(feature = "ergonomic_clones", issue = "132290")]
25262526
impl<T: ?Sized, A: Allocator + Clone> UseCloned for Rc<T, A> {}
25272527

2528+
#[unstable(feature = "share_trait", issue = "156756")]
2529+
impl<T: ?Sized, A: Allocator + Clone> Share for Rc<T, A> {}
2530+
25282531
#[cfg(not(no_global_oom_handling))]
25292532
#[stable(feature = "rust1", since = "1.0.0")]
25302533
impl<T: Default> Default for Rc<T> {

library/alloc/src/sync.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use core::any::Any;
1212
use core::cell::CloneFromCell;
1313
#[cfg(not(no_global_oom_handling))]
1414
use core::clone::TrivialClone;
15-
use core::clone::{CloneToUninit, UseCloned};
15+
use core::clone::{CloneToUninit, Share, UseCloned};
1616
use core::cmp::Ordering;
1717
use core::hash::{Hash, Hasher};
1818
use core::intrinsics::abort;
@@ -2436,6 +2436,9 @@ impl<T: ?Sized, A: Allocator + Clone> Clone for Arc<T, A> {
24362436
#[unstable(feature = "ergonomic_clones", issue = "132290")]
24372437
impl<T: ?Sized, A: Allocator + Clone> UseCloned for Arc<T, A> {}
24382438

2439+
#[unstable(feature = "share_trait", issue = "156756")]
2440+
impl<T: ?Sized, A: Allocator + Clone> Share for Arc<T, A> {}
2441+
24392442
#[stable(feature = "rust1", since = "1.0.0")]
24402443
impl<T: ?Sized, A: Allocator> Deref for Arc<T, A> {
24412444
type Target = T;

library/core/src/clone.rs

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,90 @@ pub macro Clone($item:item) {
290290
/* compiler built-in */
291291
}
292292

293+
/// A trait for types whose [`Clone`] operation creates another alias to the same
294+
/// logical resource or shared state.
295+
///
296+
/// `Share` marks types where cloning creates another handle, reference, or alias
297+
/// to the same logical resource or shared state, rather than an independent owned
298+
/// value. The distinction is semantic, not cost-based: implementing `Share` does
299+
/// not merely mean that cloning is cheap, constant-time, allocation-free, or
300+
/// convenient.
301+
///
302+
/// Calling [`share`](Share::share) is equivalent to calling [`clone`](Clone::clone)
303+
/// for implementors, but communicates that the resulting value aliases the same
304+
/// underlying resource.
305+
///
306+
/// Shared references, `Rc<T>`, `Arc<T>`, `Sender<T>`, and `SyncSender<T>` are
307+
/// examples of types that can be shared this way. Types such as `Vec<T>`,
308+
/// `String`, and `Box<T>` are not `Share` even though they implement `Clone`,
309+
/// because cloning them creates another owned value rather than another handle
310+
/// to the same logical resource.
311+
///
312+
/// # Examples
313+
///
314+
/// ```
315+
/// #![feature(share_trait)]
316+
///
317+
/// use std::cell::Cell;
318+
/// use std::clone::Share;
319+
/// use std::rc::Rc;
320+
/// use std::sync::{
321+
/// Arc,
322+
/// atomic::{AtomicUsize, Ordering},
323+
/// };
324+
///
325+
/// let value = 1;
326+
/// let reference = &value;
327+
/// assert!(std::ptr::eq(reference, reference.share()));
328+
///
329+
/// let rc = Rc::new(Cell::new(2));
330+
/// let shared_rc = rc.share();
331+
/// assert!(Rc::ptr_eq(&rc, &shared_rc));
332+
/// shared_rc.set(3);
333+
/// assert_eq!(rc.get(), 3);
334+
///
335+
/// let arc = Arc::new(AtomicUsize::new(4));
336+
/// let shared_arc = arc.share();
337+
/// assert!(Arc::ptr_eq(&arc, &shared_arc));
338+
/// shared_arc.store(5, Ordering::Relaxed);
339+
/// assert_eq!(arc.load(Ordering::Relaxed), 5);
340+
/// ```
341+
///
342+
/// ```
343+
/// #![feature(share_trait)]
344+
///
345+
/// use std::clone::Share;
346+
/// use std::sync::mpsc::{channel, sync_channel};
347+
///
348+
/// let (sender, receiver) = channel();
349+
/// let shared_sender = sender.share();
350+
/// sender.send(1).unwrap();
351+
/// shared_sender.send(2).unwrap();
352+
///
353+
/// let mut received = [receiver.recv().unwrap(), receiver.recv().unwrap()];
354+
/// received.sort();
355+
/// assert_eq!(received, [1, 2]);
356+
///
357+
/// let (sync_sender, sync_receiver) = sync_channel(2);
358+
/// let shared_sync_sender = sync_sender.share();
359+
/// sync_sender.send(3).unwrap();
360+
/// shared_sync_sender.send(4).unwrap();
361+
///
362+
/// let mut received = [sync_receiver.recv().unwrap(), sync_receiver.recv().unwrap()];
363+
/// received.sort();
364+
/// assert_eq!(received, [3, 4]);
365+
/// ```
366+
#[unstable(feature = "share_trait", issue = "156756")]
367+
pub trait Share: Clone {
368+
/// Creates another alias to the same underlying resource or shared state.
369+
///
370+
/// This is equivalent to calling [`Clone::clone`].
371+
#[unstable(feature = "share_trait", issue = "156756")]
372+
fn share(&self) -> Self {
373+
Clone::clone(self)
374+
}
375+
}
376+
293377
/// Trait for objects whose [`Clone`] impl is lightweight (e.g. reference-counted)
294378
///
295379
/// Cloning an object implementing this trait should in general:
@@ -601,7 +685,7 @@ unsafe impl CloneToUninit for crate::bstr::ByteStr {
601685
/// are implemented in `traits::SelectionContext::copy_clone_conditions()`
602686
/// in `rustc_trait_selection`.
603687
mod impls {
604-
use super::TrivialClone;
688+
use super::{Share, TrivialClone};
605689
use crate::marker::PointeeSized;
606690

607691
macro_rules! impl_clone {
@@ -689,6 +773,9 @@ mod impls {
689773
#[rustc_const_unstable(feature = "const_clone", issue = "142757")]
690774
unsafe impl<T: PointeeSized> const TrivialClone for &T {}
691775

776+
#[unstable(feature = "share_trait", issue = "156756")]
777+
impl<T: PointeeSized> Share for &T {}
778+
692779
/// Shared references can be cloned, but mutable references *cannot*!
693780
#[stable(feature = "rust1", since = "1.0.0")]
694781
impl<T: PointeeSized> !Clone for &mut T {}

library/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@
369369
#![feature(random)]
370370
#![feature(raw_os_error_ty)]
371371
#![feature(seek_io_take_position)]
372+
#![feature(share_trait)]
372373
#![feature(slice_internals)]
373374
#![feature(slice_ptr_get)]
374375
#![feature(slice_range)]

library/std/src/sync/mpsc.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@
142142
// not exposed publicly, but if you are curious about the implementation,
143143
// that's where everything is.
144144

145+
use core::clone::Share;
146+
145147
use crate::sync::mpmc;
146148
use crate::time::{Duration, Instant};
147149
use crate::{error, fmt};
@@ -645,6 +647,9 @@ impl<T> Clone for Sender<T> {
645647
}
646648
}
647649

650+
#[unstable(feature = "share_trait", issue = "156756")]
651+
impl<T> Share for Sender<T> {}
652+
648653
#[stable(feature = "mpsc_debug", since = "1.8.0")]
649654
impl<T> fmt::Debug for Sender<T> {
650655
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -774,6 +779,9 @@ impl<T> Clone for SyncSender<T> {
774779
}
775780
}
776781

782+
#[unstable(feature = "share_trait", issue = "156756")]
783+
impl<T> Share for SyncSender<T> {}
784+
777785
#[stable(feature = "mpsc_debug", since = "1.8.0")]
778786
impl<T> fmt::Debug for SyncSender<T> {
779787
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {

tests/ui/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,6 +1244,10 @@ In this directory, multiple crates are compiled, but some of them have `inline`
12441244

12451245
Tests on name shadowing.
12461246

1247+
## `tests/ui/share-trait`
1248+
1249+
Tests for the unstable `Share` trait.
1250+
12471251
## `tests/ui/shell-argfiles/`: `-Z shell-argfiles` command line flag
12481252

12491253
The `-Zshell-argfiles` compiler flag allows argfiles to be parsed using POSIX "shell-style" quoting. When enabled, the compiler will use shlex to parse the arguments from argfiles specified with `@shell:<path>`.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@ run-pass
2+
3+
#![feature(share_trait)]
4+
5+
use std::clone::Share;
6+
use std::sync::{Arc, Mutex};
7+
8+
trait Value {
9+
fn get(&self) -> i32;
10+
}
11+
12+
impl Value for Mutex<i32> {
13+
fn get(&self) -> i32 {
14+
*self.lock().unwrap()
15+
}
16+
}
17+
18+
fn main() {
19+
let value = Arc::new(Mutex::new(1));
20+
let shared = value.share();
21+
22+
assert!(Arc::ptr_eq(&value, &shared));
23+
*shared.lock().unwrap() = 2;
24+
assert_eq!(*value.lock().unwrap(), 2);
25+
26+
let dyn_value: Arc<dyn Value + Send + Sync> = Arc::new(Mutex::new(3));
27+
let shared_dyn_value = dyn_value.share();
28+
29+
assert!(Arc::ptr_eq(&dyn_value, &shared_dyn_value));
30+
assert_eq!(shared_dyn_value.get(), 3);
31+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@ run-pass
2+
3+
#![feature(share_trait)]
4+
5+
use std::clone::Share;
6+
use std::sync::mpsc::channel;
7+
8+
fn main() {
9+
let (sender, receiver) = channel();
10+
let shared_sender = sender.share();
11+
12+
sender.send(1).unwrap();
13+
shared_sender.send(2).unwrap();
14+
15+
let mut received = [receiver.recv().unwrap(), receiver.recv().unwrap()];
16+
received.sort();
17+
18+
assert_eq!(received, [1, 2]);
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@ run-pass
2+
3+
#![feature(share_trait)]
4+
5+
use std::clone::Share;
6+
use std::sync::mpsc::sync_channel;
7+
8+
fn main() {
9+
let (sender, receiver) = sync_channel(2);
10+
let shared_sender = sender.share();
11+
12+
sender.send(1).unwrap();
13+
shared_sender.send(2).unwrap();
14+
15+
let mut received = [receiver.recv().unwrap(), receiver.recv().unwrap()];
16+
received.sort();
17+
18+
assert_eq!(received, [1, 2]);
19+
}

0 commit comments

Comments
 (0)