Skip to content

Commit

Permalink
Improve Trace documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
frengor committed Mar 21, 2024
1 parent 615153b commit 2772898
Showing 1 changed file with 15 additions and 110 deletions.
125 changes: 15 additions & 110 deletions src/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ struct Foo<A: Trace + 'static, B: Trace + 'static> {
///
/// # Safety
/// The implementation of [`trace`] must uphold the following invariants:
/// * The [`trace`] implementation can trace every [`Cc`] instance owned *exclusively* by the instance of the implementing type at maximum once.
/// * The [`trace`] implementation can trace every [`Cc`] instance *exclusively* contained by the instance of the implementing type at maximum once.
/// No other [`Cc`] instance can be traced.
/// * It's always fine to panic.
/// * It's always safe to panic.
/// * During the same tracing phase, two different [`trace`] calls on the same value must *behave the same*, i.e. they must trace the same
/// [`Cc`] instances. If a panic happens during the second of such calls but not in the first one, then the [`Cc`] instances traced during
/// the second call must be a subset of the [`Cc`] instances traced in the first one.
Expand All @@ -65,121 +65,26 @@ struct Foo<A: Trace + 'static, B: Trace + 'static> {
/// * If the implementing type implements [`Drop`], then the [`Drop`] implementation must not create, clone, move, dereference, drop or call
/// any method on any [`Cc`] instance.
///
/// # Example
/// ```rust
///# use rust_cc::*;
/// struct Example {
/// an_elem: i32,
/// cc_elem: Cc<i32>,
/// optional_elem: Option<Box<i32>>,
/// optional_cc_elem: Option<Cc<i32>>,
/// }
/// To be compatible with the derive macro, the implementing type should not be a reference or raw pointer. Thus, for instance, given a `struct A`,
/// `unsafe impl Trace for &A` and `unsafe impl Trace for &mut A` should be avoided.
///
/// unsafe impl Trace for Example {
/// fn trace(&self, ctx: &mut Context) {
/// // an_elem is an i32, there's no need to trace it
/// self.cc_elem.trace(ctx);
/// // optional_elem doesn't contain a Cc, no need to trace it
/// self.optional_cc_elem.trace(ctx);
/// }
/// }
///# impl Finalize for Example {}
/// ```
/// # Implementation tips
/// It is almost always preferable to use the derive macro `#[derive(Trace)]`, but in case a manual implementation is needed the following suggestions usually apply:
/// * If a field's type implements [`Trace`], then call its [`trace`] method.
/// * Try to avoid panicking if not strictly necessary, since it may lead to memory leaks.
/// * Avoid mixing [`Cc`]s with other shared-ownership smart pointers like [`Rc`] (a [`Cc`] contained inside an [`Rc`] cannot be traced,
/// since it's not contained *exclusively*).
/// * Never tracing a field is always safe.
/// * If you need to perform any clean up actions, you should do them in the [`Finalize::finalize`] implementation (instead of inside [`Drop::drop`])
/// or using a [cleaner](crate::cleaners).
///
/// # Erroneous implementation examples
/// ```rust,no_run
///# use std::ops::Deref;
///# use rust_cc::*;
/// struct ErroneousExample {
/// cc_elem: Cc<i32>,
/// my_struct_elem: MyStruct,
/// a_cc_struct_elem: Cc<ACcStruct>,
/// ignored_cc: Cc<u64>,
/// }
///
/// struct MyStruct;
///
/// unsafe impl Trace for MyStruct {
/// fn trace(&self, _ctx: &mut Context) {
/// // No fields, no trace() methods to call
/// }
/// }
///# impl Finalize for MyStruct {}
///
/// struct ACcStruct {
/// cc: Cc<i32>,
/// }
///
/// unsafe impl Trace for ACcStruct {
/// fn trace(&self, ctx: &mut Context) {
/// self.cc.trace(ctx);
/// }
/// }
///# impl Finalize for ACcStruct {}
///
/// unsafe impl Trace for ErroneousExample {
/// fn trace(&self, ctx: &mut Context) {
/// self.cc_elem.trace(ctx); // Correct call
/// self.my_struct_elem.trace(ctx); // Correct call, although useless since MyStruct doesn't contain any Cc
///
/// let new_cc = Cc::new(10); // This is undefined behavior! ⚠️
///
/// self.a_cc_struct_elem.trace(ctx); // Correct call
/// self.a_cc_struct_elem.trace(ctx); // Double tracing of the same Cc, undefined behavior! ⚠️
///
/// // It's safe to **always** ignore a field, although this may cause memory leaks
/// // self.ignored_cc.trace(ctx);
/// }
/// }
///# impl Finalize for ErroneousExample {}
/// ```
///
/// ```rust,no_run
///# use std::cell::Cell;
///# use rust_cc::*;
/// struct Foo {
/// cc: Cell<Option<Cc<u64>>>,
/// }
///
/// unsafe impl Trace for Foo {
/// fn trace(&self, ctx: &mut Context) {
/// let _ = self.cc.take(); // Modifying self, undefined behavior! ⚠️
/// }
/// }
///# impl Finalize for Foo {}
/// ```
///
/// ```rust,no_run
///# use std::cell::RefCell;
///# use rust_cc::*;
/// struct Foo {
/// cc: RefCell<Option<Cc<u64>>>,
/// }
///
/// unsafe impl Trace for Foo {
/// fn trace(&self, ctx: &mut Context) {
/// self.cc.trace(ctx); // Correct trace implementation, but...
/// }
/// }
///# impl Finalize for Foo {}
///
/// impl Drop for Foo {
/// fn drop(&mut self) {
/// let _ = self.cc.take(); // A Cc has been moved inside Drop, undefined behavior! ⚠️
/// }
/// }
/// ```
///
/// [`self`]: https://doc.rust-lang.org/std/keyword.self.html
/// [`trace`]: crate::Trace::trace
/// [`state::is_tracing`]: crate::state::is_tracing
/// [`Finalize`]: crate::Finalize
/// [`finalize`]: crate::Finalize::finalize
/// [`Finalize::finalize`]: crate::Finalize::finalize
/// [`Cc`]: crate::Cc
/// [`Drop`]: core::ops::Drop
/// [`Rc`]: alloc::rc::Rc
/// [`Box`]: alloc::boxed::Box
/// [`drop`]: core::ops::Drop::drop
/// [`Drop::drop`]: core::ops::Drop::drop
pub unsafe trait Trace: Finalize {
fn trace(&self, ctx: &mut Context<'_>);
}
Expand Down

0 comments on commit 2772898

Please sign in to comment.