这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

Rust标准库中的标记(Marker)模块

Rust标准库中的标记(Marker)模块

原生 traits 和类型表示类型的基本属性。

Rust 类型可以根据其固有属性以各种有用的方式进行分类。 这些分类表示为 traits。

https://doc.rust-lang.org/std/marker/index.html

1 - Sized trait

Sized trait用于标记在编译时已知常量大小的类型。

1.1 - Sized trait的std文档

Sized trait的std文档

https://doc.rust-lang.org/std/marker/trait.Sized.html

pub trait Sized { }

在编译时已知常量大小的类型。

所有类型参数的隐含边界均为 Sized。如果不合适,可以使用特殊语法 ?Sized 删除此绑定。

struct Foo<T>(T);
struct Bar<T: ?Sized>(T);

// struct FooUse(Foo<[i32]>); // 错误:没有为 [i32] 实现大小调整
struct BarUse(Bar<[i32]>); // OK

一个例外是 trait 的隐式 Self 类型。 trait 没有隐式 Sized 绑定,因为它与 trait 对象 不兼容,根据定义,trait 需要与所有可能的实现者一起使用,因此可以为任意大小。

尽管 Rust 允许您将 Sized 绑定到 trait,但是以后您将无法使用它来形成 trait 对象:

trait Foo { }
trait Bar: Sized { }

struct Impl;
impl Foo for Impl { }
impl Bar for Impl { }

let x: &dyn Foo = &Impl;    // OK
// let y: &dyn Bar = &Impl; // 错误:无法将 trait `Bar` 创建成对象

备注:trait object的要求之一就是trait不能是Sized,这也可以作为禁止将某个trait用作trait object的方式

1.2 - Sized trait的源码

Sized trait的

https://github.com/rust-lang/rust/blob/master/library/core/src/marker.rs

#[stable(feature = "rust1", since = "1.0.0")]
#[lang = "sized"]
#[rustc_on_unimplemented(
    message = "the size for values of type `{Self}` cannot be known at compilation time",
    label = "doesn't have a size known at compile-time"
)]
#[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable
#[rustc_specialization_trait]
pub trait Sized {
    // Empty.
}

1.3 - [Rust编程之道笔记]Sided trait

Rust编程之道一书 3.4.4 标签trait

Sized trait 非常重要,编译期用它来识别在编译期确定大小的类型。

#[lang = "sized"]
pub trait Sized {
    // Empty.
}

Sized trait 是空 trait,仅仅作为标签 trait 供编译期使用。真正起"打标签"作用的是属性 #[lang = "sized"]。该属性lang表示Sized trait供rust语言本身使用,声明为 “sized”,称为语言项(Lang Item)。

rust语言中大部分类型都是默认 Sized ,如果需要使用动态大小类型,则需要改为 <T: ?Sized> 限定。

struct Foo<T>(T);
struct Bar<T: ?Sized>(T);

Sized, Unsize和 ?Sized的关系

  • Sized 标记的是在编译期可确定大小的类型

  • Unsized 标记的是动态大小类型,在编译期无法确定其大小

    目前rust中的动态类型有 trait 和 [T]

    其中 [T] 代表一定数量的T在内存中的一次排列,但不知道具体的数量,所以大小是未知的。

  • ?Sized 标记的类型包含了 Sized 和 Unsized 所标识的两种类型。

    所以泛型结构体 struct Bar<T: ?Sized>(T); 支持编译期可确定大小类型和动态大小类型两种类型。

动态大小类型的限制规则

  • 只可以通过胖指针来操作 Unsize 类型,如 &[T] 或者 &trait
  • 变量,参数和枚举变量不能使用动态大小类型
  • 结构体中只有最有一个字段可以使用动态大小类型,其他字段不可以使用

2 - Unsized trait

可以是未定义大小的类型也可以是动态大小的类型。

2.1 - Unsize trait的std文档

Unsize trait的std文档

https://doc.rust-lang.org/std/marker/trait.Unsize.html

pub trait Unsize<T: ?Sized> {}

可以是未定义大小的类型也可以是动态大小的类型。

例如,按大小排列的数组类型 [i8; 2] 实现 Unsize<[i8]>Unsize<dyn fmt::Debug>

Unsize 的所有实现均由编译器自动提供。

Unsize 为以下目的实现:

  • [T; N]Unsize<[T]>

  • T: TraitTUnsize<dyn Trait>

  • Foo<..., T, ...>Unsize<Foo<..., U, ...>>,如果

    • T: Unsize<U>
    • Foo 是一个结构体
    • Foo 的最后一个字段具有涉及 T 的类型
    • T 不属于任何其他字段的类型
    • Bar<T>: Unsize<Bar<U>>, 如果 Foo 的最后一个字段的类型为 Bar<T>

Unsizeops::CoerceUnsized 一起使用可允许 “user-defined” 容器 (例如 Rc 包含动态大小的类型。 有关更多详细信息,请参见 DST coercion RFCthe nomicon entry on coercion

2.2 - Unsize trait的源码

Unsize trait的源码

https://github.com/rust-lang/rust/blob/master/library/core/src/marker.rs

#[unstable(feature = "unsize", issue = "27732")]
#[lang = "unsize"]
pub trait Unsize<T: ?Sized> {
    // Empty.
}

3 - Copy trait

只需复制位即可复制其值的类型。

3.1 - Copy trait的std文档

Copy trait的std文档

https://doc.rust-lang.org/std/marker/trait.Copy.html

pub trait Copy: Clone { }

只需复制位即可复制其值的类型。

默认情况下,变量绑定具有 move语义。换句话说:

#[derive(Debug)]
struct Foo;

let x = Foo;

let y = x;

// `x` 已移至 `y`,因此无法使用

// println!("{:?}", x); // error: use of moved value

但是,如果类型实现 Copy,则它具有复制语义:

// 我们可以派生一个 `Copy` 实现。
// `Clone` 也是必需的,因为它是 `Copy` 的父特征。
#[derive(Debug, Copy, Clone)]
struct Foo;

let x = Foo;

let y = x;

// `y` 是 `x` 的副本

println!("{:?}", x); // A-OK!

重要的是要注意,在这两个示例中,唯一的区别是分配后是否允许您访问 x。 在后台,复制(copy)和移动(move)都可能导致将位复制到内存中,尽管有时会对其进行优化。

如何实现 Copy?

有两种方法可以在您的类型上实现 Copy。最简单的是使用 derive:

#[derive(Copy, Clone)]
struct MyStruct;

您还可以手动实现 CopyClone:

struct MyStruct;

impl Copy for MyStruct { }

impl Clone for MyStruct {
    fn clone(&self) -> MyStruct {
        *self
    }
}

两者之间的区别很小: derive 策略还将 Copy 绑定在类型参数上,这并不总是需要的。

CopyClone 有什么区别?

复制是隐式发生的,例如作为分配 y = x 的一部分。Copy 的行为不可重载; 它始终是简单的按位复制。

克隆是一个明确的动作 x.clone()Clone 的实现可以提供安全复制值所需的任何特定于类型的行为。 例如,用于 StringClone 的实现需要在堆中复制指向字符串的缓冲区。 String 值的简单按位副本将仅复制指针,从而导致该行向下双重释放。 因此,StringClone,但不是 Copy

CloneCopy 的父特征,因此 Copy 的所有类型也必须实现 Clone。 如果类型为 Copy,则其 Clone 实现仅需要返回 *self (请参见上面的示例)。

类型何时可以是 Copy?

如果类型的所有组件都实现 Copy,则它可以实现 Copy。例如,此结构体可以是 Copy:

#[derive(Copy, Clone)]
struct Point {
   x: i32,
   y: i32,
}

一个结构体可以是 Copy,而 i32Copy,因此 Point 有资格成为 Copy。 相比之下,考虑

struct PointList {
    points: Vec<Point>,
}

结构体 PointList 无法实现 Copy,因为 Vec 不是 Copy。如果尝试派生 Copy 实现,则会收到错误消息:

the trait `Copy` may not be implemented for this type; field `points` does not implement `Copy`

共享引用 (&T) 也是 Copy,因此,即使类型中包含不是 *Copy 类型的共享引用 T,也可以是 Copy。 考虑下面的结构体,它可以实现 Copy,因为它从上方仅对我们的非 Copy 类型 PointList 持有一个 shared 引用:

#[derive(Copy, Clone)]
struct PointListWrapper<'a> {
    point_list_ref: &'a PointList,
}

什么时候类型不能为 Copy?

某些类型无法安全复制。例如,复制 &mut T 将创建一个别名可变引用。 复制 String 将重复管理 String 缓冲区,从而导致双重释放。

概括后一种情况,任何实现 Drop 的类型都不能是 Copy,因为它除了管理自己的 size_of:: 字节外还管理一些资源。

果您尝试在包含非 Copy 数据的结构或枚举上实现 Copy,则会收到 E0204 错误。

什么时候类型应该是 Copy?

一般来说,如果您的类型可以实现 Copy,则应该这样做。 但是请记住,实现 Copy 是您类型的公共 API 的一部分。 如果该类型将来可能变为非 Copy,则最好现在省略 Copy 实现,以避免 API 发生重大更改。

其他实现者

除下面列出的实现者外,以下类型还实现 Copy:

  • 函数项类型 (即,为每个函数定义的不同类型)
  • 函数指针类型 (例如 fn() -> i32)
  • 如果项类型也实现 Copy (例如 [i32; 123456]),则所有大小的数组类型
  • 如果每个组件还实现 Copy (例如 ()(i32, bool)),则为元组类型
  • 闭包类型,如果它们没有从环境中捕获任何值,或者所有此类捕获的值本身都实现了 Copy。 请注意,由共享引用捕获的变量始终实现 Copy (即使引用对象没有实现),而由变量引用捕获的变量从不实现 Copy

3.2 - Copy trait的源码

Copy trait的源码

https://github.com/rust-lang/rust/blob/master/library/core/src/marker.rs

#[stable(feature = "rust1", since = "1.0.0")]
#[lang = "copy"]
#[rustc_unsafe_specialization_marker]
pub trait Copy: Clone {
    // Empty.
}

原始类型的Copy的实现。

无法在Rust中描述的实现是在 rustc_trait_selection 中的 traits::SelectionContext::copy_clone_conditions() 中实现。

mod copy_impls {

    use super::Copy;

    macro_rules! impl_copy {
        ($($t:ty)*) => {
            $(
                #[stable(feature = "rust1", since = "1.0.0")]
                impl Copy for $t {}
            )*
        }
    }

    impl_copy! {
        usize u8 u16 u32 u64 u128
        isize i8 i16 i32 i64 i128
        f32 f64
        bool char
    }

    #[unstable(feature = "never_type", issue = "35121")]
    impl Copy for ! {}

    #[stable(feature = "rust1", since = "1.0.0")]
    impl<T: ?Sized> Copy for *const T {}

    #[stable(feature = "rust1", since = "1.0.0")]
    impl<T: ?Sized> Copy for *mut T {}

    /// Shared references can be copied, but mutable references *cannot*!
    #[stable(feature = "rust1", since = "1.0.0")]
    impl<T: ?Sized> Copy for &T {}
}

3.3 - [Rust编程之道笔记]Copy trait

Rust编程之道一书 3.4.4 标签trait

copy trait 用来标识可以按位复制其值的类型,按位复制等价于c语言中的 memcpy。

pub trait Copy: Clone { }

copy trait 继承自 clone trait,意味着要实现 Copy trait 的类型,必须实现 Clone trait 中定义的方法。

要实现 Copy trait,就必须同时实现 Clone trait。

struct MyStruct;

impl Copy for MyStruct { }

impl Clone for MyStruct {
    fn clone(&self) -> MyStruct {
        *self
    }
}

rust提供了更方便的 derive 属性:

#[derive(Copy, Clone)]
struct MyStruct;

copy 的行为是隐式行为,开发者不能重载 Copy 行为,它永远都是一个简单的位复制。

并非所有的类型都可以实现 Copy trait。

4 - Send trait

可以跨线程边界传输的类型。

4.1 - Send trait的源码

Send trait的源码

https://github.com/rust-lang/rust/blob/master/library/core/src/marker.rs

#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "send_trait")]
#[rustc_on_unimplemented(
    message = "`{Self}` cannot be sent between threads safely",
    label = "`{Self}` cannot be sent between threads safely"
)]
pub unsafe auto trait Send {
    // empty.
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> !Send for *const T {}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> !Send for *mut T {}

4.2 - Send trait的std文档

Send trait的std文档

https://doc.rust-lang.org/std/marker/trait.Send.html

pub unsafe auto trait Send { }

可以跨线程边界传输的类型。

当编译器确定适当时,会自动实现此 trait。

Send 类型的一个示例是引用计数指针 rc::Rc。 如果两个线程试图克隆指向相同引用计数值的 Rc,它们可能会同时尝试更新引用计数,这是 未定义行为 因为 Rc 不使用原子操作。

它的表亲 sync::Arc 确实使用原子操作 (产生一些开销),因此它是 Send

有关更多详细信息,请参见 the Nomicon

4.3 - [Rust编程之道笔记]Send trait

Rust编程之道一书 3.4.4 标签trait

rust 提供了 Send 和 Sync 两个标签 trait,他们是 rust 无数据竞争并发的基石。

  • 实现了 Send 的类型,可以安全的在线程间传递值,也就是说可以跨线程传递所有权
  • 实现了 Sync 的类型,可以跨线程安全地传递共享(不可变)引用

有了这两个标签,就可以把rust中所有的类型归为两类:

  1. 可以安全跨线程传递的值和引用
  2. 不可以安全跨线程传递的值和引用

在配合所有权机制,带来的效果就是,rust 能够在编译期就检查出数据竞争的隐患,而不需要到运行时再排查。

# 备注:这行代码在 marker.rs 中已经找不到了,不知道
# 为所有类型实现 Send
unsafe impl Send for .. {}

#[stable(feature = "rust1", since = "1.0.0")]
# 使用 !Send 语法排除  *const T  *mut T
impl<T: ?Sized> !Send for *const T {}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> !Send for *mut T {}

4.4 - send和sync

The Rustonomicon文档中的 send and sync 一节

https://doc.rust-lang.org/nomicon/send-and-sync.html

不过,并不是所有的东西都服从于继承的可变性。有些类型允许你在内存中对一个位置有多个别名,同时对其进行突变。除非这些类型使用同步化来管理这种访问,否则它们绝对不是线程安全的。Rust通过Send和Sync特性捕捉到这一点。

  • 如果将一个类型发送到另一个线程是安全的,那么它就是Send。
  • 如果一个类型可以安全地在线程间共享,那么它就是 Sync(当且仅当 &T 是 Send 时,T 是 Sync)。

Send 和 Sync 是Rust的并发故事的基础。因此,存在大量的特殊工具来使它们正常工作。首先,它们是不安全的特性。这意味着它们的实现是不安全的,而其他不安全的代码可以认为它们是正确实现的。由于它们是标记性的特征(它们没有像方法那样的关联项),正确实现仅仅意味着它们具有实现者应该具有的内在属性。不正确地实现 Send 或 Sync 会导致未定义行为。

Send 和 Sync 也是自动派生的特性。这意味着,与其它特质不同,如果一个类型完全由 Send 或 Sync 类型组成,那么它就是 Send 或 Sync。几乎所有的基元都是 Send 和 Sync,因此,几乎所有你将与之交互的类型都是 Send 和 Sync。

主要的例外情况包括:

  • 原始指针既不是 Send 也不是 Sync(因为它们没有安全防护)。
  • UnsafeCell 不是Sync(因此Cell和RefCell也不是)。
  • Rc不是Send或Sync(因为refcount是共享的,而且是不同步的)。

Rc 和 UnsafeCell 从根本上说不是线程安全的:它们启用了非同步的共享可变体状态。然而,严格来说,原始指针被标记为线程不安全,更像是一种提示。对原始指针做任何有用的事情都需要对其进行解引用,这已经是不安全的了。从这个意义上说,人们可以争辩说,将它们标记为线程安全是 “好的”。

然而,重要的是,它们不是线程安全的,以防止包含它们的类型被自动标记为线程安全的。这些类型有非实质性的未跟踪的所有权,它们的作者不可能认真考虑线程安全问题。在Rc的例子中,我们有一个包含 *mut 的类型的好例子,它绝对不是线程安全的。

如果需要的话,那些没有自动派生的类型可以简单地实现它们。

struct MyBox(*mut u8);

unsafe impl Send for MyBox {}
unsafe impl Sync for MyBox {}

在极其罕见的情况下,一个类型被不适当地自动派生为Send或Sync,那么我们也可以不实现Send和Sync。

#![feature(negative_impls)]

// I have some magic semantics for some synchronization primitive!
struct SpecialThreadToken(u8);

impl !Send for SpecialThreadToken {}
impl !Sync for SpecialThreadToken {}

注意,就其本身而言,不可能错误地派生出 Send 和 Sync。只有那些被其他不安全代码赋予特殊含义的类型才有可能通过错误的 Send 或 Sync 引起麻烦。

大多数对原始指针的使用应该被封装在一个足够的抽象后面,以便 Send 和 Sync 可以被派生。例如,所有Rust的标准集合都是 Send 和 Sync(当它们包含Send和Sync类型时),尽管它们普遍使用原始指针来管理分配和复杂的所有权。同样地,大多数进入这些集合的迭代器都是Send和Sync的,因为它们在很大程度上表现为进入集合的 &&mut

例子

由于各种原因,Box被编译器实现为它自己的特殊内在类型,但是我们可以自己实现一些具有类似行为的东西,看看什么时候实现 Send 和 Sync 是合理的。让我们把它叫做 “Carton”。

我们先写代码,把一个分配在栈上的值,转移到堆上。


#![allow(unused)]
fn main() {
pub mod libc {
   pub use ::std::os::raw::{c_int, c_void};
   #[allow(non_camel_case_types)]
   pub type size_t = usize;
   extern "C" { pub fn posix_memalign(memptr: *mut *mut c_void, align: size_t, size: size_t) -> c_int; }
}
use std::{
    mem::{align_of, size_of},
    ptr,
};

struct Carton<T>(ptr::NonNull<T>);

impl<T> Carton<T> {
    pub fn new(value: T) -> Self {
        // Allocate enough memory on the heap to store one T.
        assert_ne!(size_of::<T>(), 0, "Zero-sized types are out of the scope of this example");
        let mut memptr = ptr::null_mut() as *mut T;
        unsafe {
            let ret = libc::posix_memalign(
                (&mut memptr).cast(),
                align_of::<T>(),
                size_of::<T>()
            );
            assert_eq!(ret, 0, "Failed to allocate or invalid alignment");
        };

        // NonNull is just a wrapper that enforces that the pointer isn't null.
        let mut ptr = unsafe {
            // Safety: memptr is dereferenceable because we created it from a
            // reference and have exclusive access.
            ptr::NonNull::new(memptr.cast::<T>())
                .expect("Guaranteed non-null if posix_memalign returns 0")
        };

        // Move value from the stack to the location we allocated on the heap.
        unsafe {
            // Safety: If non-null, posix_memalign gives us a ptr that is valid
            // for writes and properly aligned.
            ptr.as_ptr().write(value);
        }

        Self(ptr)
    }
}
}

这不是很有用,因为一旦我们的用户给了我们一个值,他们就没有办法访问它。Box实现了 DerefDerefMut,这样你就可以访问内部的值。让我们来做这件事。


#![allow(unused)]
fn main() {
use std::ops::{Deref, DerefMut};

struct Carton<T>(std::ptr::NonNull<T>);

impl<T> Deref for Carton<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        unsafe {
            // Safety: The pointer is aligned, initialized, and dereferenceable
            //   by the logic in [`Self::new`]. We require writers to borrow the
            //   Carton, and the lifetime of the return value is elided to the
            //   lifetime of the input. This means the borrow checker will
            //   enforce that no one can mutate the contents of the Carton until
            //   the reference returned is dropped.
            self.0.as_ref()
        }
    }
}

impl<T> DerefMut for Carton<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe {
            // Safety: The pointer is aligned, initialized, and dereferenceable
            //   by the logic in [`Self::new`]. We require writers to mutably
            //   borrow the Carton, and the lifetime of the return value is
            //   elided to the lifetime of the input. This means the borrow
            //   checker will enforce that no one else can access the contents
            //   of the Carton until the mutable reference returned is dropped.
            self.0.as_mut()
        }
    }
}
}

最后,让我们考虑一下我们的Carton是否是Send和Sync。有些东西可以安全地成为 “Send”,除非它与其他东西共享可变的状态而不对其进行排他性访问。每个Carton都有一个唯一的指针,所以我们很好。

// Safety: No one besides us has the raw pointer, so we can safely transfer the
// Carton to another thread if T can be safely transferred.
unsafe impl<T> Send for Carton<T> where T: Send {}

Sync如何?为了使Carton同步,我们必须强制规定,你不能写到存储在一个 &Carton 中的东西,而这个东西可以从另一个 &Carton中 读到或写入。由于你需要一个 &mut Carton 来写入指针,并且 borrow 检查器强制要求可变引用必须是独占的,所以Carton也不存在健全性问题,可以同步。

// Safety: Since there exists a public way to go from a `&Carton<T>` to a `&T`
// in an unsynchronized fashion (such as `Deref`), then `Carton<T>` can't be
// `Sync` if `T` isn't.
// Conversely, `Carton` itself does not use any interior mutability whatsoever:
// all the mutations are performed through an exclusive reference (`&mut`). This
// means it suffices that `T` be `Sync` for `Carton<T>` to be `Sync`:
unsafe impl<T> Sync for Carton<T> where T: Sync  {}

当我们断言我们的类型是Send and Sync时,我们通常需要强制要求每个包含的类型都是Send and Sync。当编写行为像标准库类型的自定义类型时,我们可以断言我们有同样的要求。例如,下面的代码断言,如果同类型的Box是Send,那么Carton就是Send,在这种情况下,这就等于说T是Send。

unsafe impl<T> Send for Carton<T> where Box<T>: Send {}

现在,Carton有一个内存泄漏,因为它从不释放它分配的内存。一旦我们解决了这个问题,我们就必须确保满足一个新的要求:我们需要知道free可以在一个指针上被调用,而这个指针是由另一个线程分配的。我们可以在libc::free的文档中检查这一点是否正确。

impl<T> Drop for Carton<T> {
    fn drop(&mut self) {
        unsafe {
            libc::free(self.0.as_ptr().cast());
        }
    }
}

一个不发生这种情况的好例子是MutexGuard:注意它不是Send。MutexGuard的实现使用了一些库,这些库要求你确保你不会试图释放你在不同线程中获得的锁。如果你能够将MutexGuard发送到另一个线程,那么析构器就会在你发送它的线程中运行,这就违反了要求。MutexGuard仍然可以被同步,因为你能发送给另一个线程的只是一个&MutexGuard,而丢弃一个引用什么也做不了。

TODO:更好地解释什么可以或不可以是Send或Sync。仅仅针对数据竞赛就足够了吗?

备注: 没看懂。。。

5 - Sync trait

可以在线程之间安全共享引用的类型。

5.1 - Sync trait的std文档

Sync trait的std文档

https://doc.rust-lang.org/std/marker/trait.Sync.html

pub unsafe auto trait Sync { }

可以在线程之间安全共享引用的类型。

当编译器确定适当时,会自动实现此 trait。

精确的定义是:当且仅当 &TSend 时,类型 T 才是 Sync。 换句话说,如果在线程之间传递 &T 引用时没有 未定义的行为 (包括数据竞争) 的可能性。

正如人们所料,像 u8f64 这样的原始类型都是 Sync,包含它们的简单聚合类型也是如此,比如元组、结构体和枚举。 基本 Sync 类型的更多示例包括不可变类型 (例如 &T) 以及具有简单继承的可变性的类型,例如 BoxVec 和大多数其他集合类型。

(泛型参数必须为 Sync,容器才能 [Sync]。)

该定义的一个令人惊讶的结果是 &mut TSync (如果 TSync),即使看起来可能提供了不同步的可变的。 诀窍是,共享引用 (即 & &mut T) 后面的可变引用将变为只读,就好像它是 & &T 一样。 因此,没有数据竞争的风险。

不是 Sync 的类型是具有非线程安全形式的 “内部可变性” 的类型,例如 CellRefCell。 这些类型甚至允许通过不可变,共享引用来更改其内容。 例如,Cell 上的 set 方法采用 &self,因此它仅需要共享的引用 &Cell。 该方法不执行同步,因此 Cell 不能为 Sync

另一个非 Sync 类型的例子是引用计数指针 Rc。 给定任何引用 &Rc,您可以克隆新的 Rc,以非原子方式修改引用计数。

对于确实需要线程安全的内部可变性的情况,Rust 提供 原子数据类型 以及通过 sync::Mutexsync::RwLock 进行的显式锁定。 这些类型可确保任何可变的都不会引起数据竞争,因此类型为 Sync。 同样,sync::Arc 提供了 Rc 的线程安全模拟。

任何具有内部可变性的类型还必须在 value(s) 周围使用 cell::UnsafeCell 包装器,该包装器可以通过共享的引用进行更改。 未定义的行为 无法做到这一点。 例如,从 &T&mut Ttransmute 无效。

有关 Sync 的更多详细信息,请参见 the Nomicon

5.2 - Sync trait的源码

Sync trait的源码

https://github.com/rust-lang/rust/blob/master/library/core/src/marker.rs

#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "sync_trait")]
#[lang = "sync"]
#[rustc_on_unimplemented(
    message = "`{Self}` cannot be shared between threads safely",
    label = "`{Self}` cannot be shared between threads safely"
)]
pub unsafe auto trait Sync {
    // Empty
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> !Sync for *const T {}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> !Sync for *mut T {}

6 - Unpin trait

固定后可以安全移动的类型。

6.1 - Unsize trait的std文档

Unsize trait的std文档

https://doc.rust-lang.org/std/marker/trait.Unpin.html

pub auto trait Unpin { }

固定后可以安全移动的类型。

Rust 本身没有固定类型的概念,并认为 move (例如,通过赋值或 mem::replace 始终是安全的。

Pin 类型代替使用,以防止在类型系统中移动。Pin<P<T>> 包装器中包裹的指针 P<T> 不能移出。 有关固定的更多信息,请参见 pin module 文档。

T 实现 Unpin trait 消除了固定该类型的限制,然后允许使用诸如 mem::replace 之类的功能将 TPin<P<T>> 中移出。

Unpin 对于非固定数据完全没有影响。 特别是,mem::replace 可以愉快地移动 !Unpin 数据 (它适用于任何 &mut T,而不仅限于 T: Unpin)。 但是,您不能对包装在 Pin<P<T>> 内的数据使用 mem::replace,因为您无法获得所需的 &mut T,并且 that 是使此系统正常工作的原因。

因此,例如,这只能在实现 Unpin 的类型上完成:

use std::mem;
use std::pin::Pin;

let mut string = "this".to_string();
let mut pinned_string = Pin::new(&mut string);

// 我们需要一个可变引用来调用 `mem::replace`。
// 我们可以通过 (implicitly) 调用 `Pin::deref_mut` 来获得这样的引用,但这仅是可能的,因为 `String` 实现了 `Unpin`。
mem::replace(&mut *pinned_string, "other".to_string());

trait 几乎针对每种类型自动实现。

6.2 - Unpin trait的源码

Unpin trait的源码

https://github.com/rust-lang/rust/blob/master/library/core/src/marker.rs

#[stable(feature = "pin", since = "1.33.0")]
#[rustc_on_unimplemented(
    note = "consider using `Box::pin`",
    message = "`{Self}` cannot be unpinned"
)]
#[lang = "unpin"]
pub auto trait Unpin {}


/// A marker type which does not implement `Unpin`.
///
/// If a type contains a `PhantomPinned`, it will not implement `Unpin` by default.
#[stable(feature = "pin", since = "1.33.0")]
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct PhantomPinned;

#[stable(feature = "pin", since = "1.33.0")]
impl !Unpin for PhantomPinned {}

#[stable(feature = "pin", since = "1.33.0")]
impl<'a, T: ?Sized + 'a> Unpin for &'a T {}

#[stable(feature = "pin", since = "1.33.0")]
impl<'a, T: ?Sized + 'a> Unpin for &'a mut T {}

#[stable(feature = "pin_raw", since = "1.38.0")]
impl<T: ?Sized> Unpin for *const T {}

#[stable(feature = "pin_raw", since = "1.38.0")]
impl<T: ?Sized> Unpin for *mut T {}