Git Product home page Git Product logo

self_cell's Introduction

github crates.io docs.rs

self_cell!

Use the macro-rules macro: self_cell! to create safe-to-use self-referential structs in stable Rust, without leaking the struct internal lifetime.

In a nutshell, the API looks roughly like this:

// User code:

self_cell!(
    struct NewStructName {
        owner: Owner,

        #[covariant]
        dependent: Dependent,
    }
    
    impl {Debug}
);

// Generated by macro:

struct NewStructName(...);

impl NewStructName {
    fn new(
        owner: Owner,
        dependent_builder: impl for<'a> FnOnce(&'a Owner) -> Dependent<'a>
    ) -> NewStructName { ... }
    fn borrow_owner<'a>(&'a self) -> &'a Owner { ... }
    fn borrow_dependent<'a>(&'a self) -> &'a Dependent<'a> { ... }
}

impl Debug for NewStructName { ... }

Self-referential structs are currently not supported with safe vanilla Rust. The only reasonable safe alternative is to expect the user to juggle 2 separate data structures which is a mess. The library solution ouroboros is really expensive to compile due to its use of procedural macros.

This alternative is no_std, uses no proc-macros, some self contained unsafe and works on stable Rust, and is miri tested. With a total of less than 300 lines of implementation code, which consists mostly of type and trait implementations, this crate aims to be a good minimal solution to the problem of self-referential structs.

It has undergone community code review from experienced Rust users.

Fast compile times

$ rm -rf target && cargo +nightly build -Z timings

Compiling self_cell v0.9.0
Completed self_cell v0.9.0 in 0.2s

Because it does not use proc-macros, and has 0 dependencies compile-times are fast.

Measurements done on a slow laptop.

A motivating use case

use self_cell::self_cell;

#[derive(Debug, Eq, PartialEq)]
struct Ast<'a>(pub Vec<&'a str>);

self_cell!(
    struct AstCell {
        owner: String,

        #[covariant]
        dependent: Ast,
    }

    impl {Debug, Eq, PartialEq}
);

fn build_ast_cell(code: &str) -> AstCell {
    // Create owning String on stack.
    let pre_processed_code = code.trim().to_string();

    // Move String into AstCell, then build Ast inplace.
    AstCell::new(
        pre_processed_code,
        |code| Ast(code.split(' ').filter(|word| word.len() > 1).collect())
    )
}

fn main() {
    let ast_cell = build_ast_cell("fox = cat + dog");

    println!("ast_cell -> {:?}", &ast_cell);
    println!("ast_cell.borrow_owner() -> {:?}", ast_cell.borrow_owner());
    println!("ast_cell.borrow_dependent().0[1] -> {:?}", ast_cell.borrow_dependent().0[1]);
}
$ cargo run

ast_cell -> AstCell { owner: "fox = cat + dog", dependent: Ast(["fox", "cat", "dog"]) }
ast_cell.borrow_owner() -> "fox = cat + dog"
ast_cell.borrow_dependent().0[1] -> "cat"

There is no way in safe Rust to have an API like build_ast_cell, as soon as Ast depends on stack variables like pre_processed_code you can't return the value out of the function anymore. You could move the pre-processing into the caller but that gets ugly quickly because you can't encapsulate things anymore. Note this is a somewhat niche use case, self-referential structs should only be used when there is no good alternative.

Under the hood, it heap allocates a struct which it initializes first by moving the owner value to it and then using the reference to this now Pin/Immovable owner to construct the dependent inplace next to it. This makes it safe to move the generated SelfCell but you have to pay for the heap allocation.

See the documentation for a more in-depth API overview and advanced examples: https://docs.rs/self_cell

Installing

See cargo docs.

Running the tests

cargo test

cargo miri test

Related projects

Min required rustc version

By default the minimum required rustc version is 1.51.

There is an optional feature you can enable called "old_rust" that enables support down to rustc version 1.36. However this requires polyfilling std library functionality for older rustc with technically UB versions. Testing does not show older rustc versions (ab)using this. Use at your own risk.

The minimum versions are best-effort and may change with any new major release.

Contributing

Please respect the CODE_OF_CONDUCT.md when contributing.

Versioning

We use SemVer for versioning. For the versions available, see the tags on this repository.

Authors

See also the list of contributors who participated in this project.

License

This project is licensed under the Apache License, Version 2.0 - see the LICENSE.md file for details.

self_cell's People

Contributors

jplatte avatar kornelski avatar link2xt avatar obi1kenobi avatar soniex2 avatar steffahn avatar subpop avatar voultapher avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

self_cell's Issues

Possible to have a lifetime in owner?

I have the following situation:

enum Thingy<'a, F: Fingerprint> {
    Ref(&'a F),
    Partial(ThingPart<'a, F>)
}

impl<'a, F: Fingerprint> Fingerprint for Thingy<'a, F> {}

struct Intermediary<'a, F: Fingerprint>(Vec<Thingy<'a, F>>)

struct Leaf<'a, F: Fingerprint> {
    items: &'a [F],
    expensive: ComputedThing
}

impl<'a, F: Fingerprint> TryFrom<&'a [F]> for Leaf<'a, F> {}

Is there a way to make a self_cell containing Intermediary<'a, F> as the owner and Leaf<'_, Thingy<'a, F>> as the borrower? I'm fairly sure the macro can't do it today, but I'm less sure if there's a fundamental reason this can't work, if it's possible and outside the crate's capabilities, or if the crate can do it and I just don't know how to ask for it.

Incorrect drop order for `JoinedCell`

use self_cell::self_cell;

struct StrRef<'a>(&'a str);
impl Drop for StrRef<'_> {
    fn drop(&mut self) {
        println!("{}", self.0);
    }
}

self_cell! {
    struct Foo {
        owner: String,
        #[covariant]
        dependent: StrRef,
    }
}

fn main() {
    let foo = Foo::new("Hello World".into(), |s| StrRef(s));
} // prints garbage

new/try_new with mut owner as lambda parameter

currently new and try new expects a lambda that accepts a ref on the owner to produce a dependent object but I need to have mut reference on the owner to produce the dependent object. I use rusqlite connection as the owner and inner self_cell as the dependent object. I use statement as the inner owner to produce dependent rows. So I need the statement to be mut in order to have rows.

        use sql::Rows;
        self_cell::self_cell! {
            struct RowsCell<'a> {
                owner: sql::Statement<'a>,
                #[covariant]
                dependent: Rows,
            }
        }

        self_cell::self_cell! {
            struct StatementCell {
                owner: sql::Connection,
                #[covariant]
                dependent: RowsCell,
            }
        }
                ----------------------
                self.stmt = Some(StatementCell::try_new(conn, |conn| {
                    RowsCell::try_new(conn.prepare(query)?, |stmt| {
                        (unsafe { &mut *(stmt as *const sql::Statement as *mut sql::Statement) })
                            .query(sql::params_from_iter(values))
                            .and_then(|mut r| {
                                r.advance()?;
                                Ok(r)
                            })
                    })
                })?);

as you can see I simply cast ref to mut ref and everything worked fine until I updated my toolchain to the latest version, and now I have this error: casting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell

It seems to me the restriction to have ref owner in new/try_new is artificial and doesn't have any real reason. Am I wrong? If not would you please consider to lift the restriction?

Hygienic bug in macro

self_cell doesn't allow a custom result export:

use self_cell::self_cell;

// Remove this, and it compiles
type Result<T> = std::result::Result<T, ()>;

#[derive(Debug, Eq, PartialEq)]
struct Ast<'a>(pub Vec<&'a str>);

self_cell!(
    struct AstCell {
        owner: String,

        #[covariant]
        dependent: Ast,
    }

    impl {Debug, Eq, PartialEq}
);

fn build_ast_cell(code: &str) -> AstCell {
    // Create owning String on stack.
    let pre_processed_code = code.trim().to_string();

    // Move String into AstCell, then build Ast inplace.
    AstCell::new(pre_processed_code, |code| {
        Ast(code.split(' ').filter(|word| word.len() > 1).collect())
    })
}

fn main() {
    let ast_cell = build_ast_cell("fox = cat + dog");

    println!("ast_cell -> {:?}", &ast_cell);
    println!("ast_cell.borrow_owner() -> {:?}", ast_cell.borrow_owner());
    println!(
        "ast_cell.borrow_dependent().0[1] -> {:?}",
        ast_cell.borrow_dependent().0[1]
    );
}

The `unsafe_self_cell` field is accessible, allowing UB from safe code

use self_cell::self_cell;

type Dep1<'a> = (&'a str, &'static str);

self_cell! {
    pub struct Struct1 {
        owner: String,
        #[covariant]
        dependent: Dep1,
    }
}

type Dep2<'a> = (&'static str, &'a str);

self_cell! {
    pub struct Struct2 {
        owner: String,
        #[covariant]
        dependent: Dep2,
    }
}

fn main() {
    let hello: &'static str;
    {
        let mut x1 = Struct1::new(String::from("Hello World"), |s| (s, ""));
        let mut x2 = Struct2::new(String::new(), |_| ("", ""));
        std::mem::swap(&mut x1.unsafe_self_cell, &mut x2.unsafe_self_cell);
        hello = x2.borrow_dependent().0;

        dbg!(hello); // "Hello World"
        // hello is now a static reference in to the "Hello World" string
    }
    // the String is dropped at the end of the block above

    dbg!(hello); // prints garbage, use-after-free
}

Unsound Send and Sync impls when the borrowed type is not

Filing to track the bug reported by @jDomantas in https://www.reddit.com/r/rust/comments/m42fjx/safetouse_procmacrofree_selfreferential_structs/gqt3i35/, which can result in UB in safe code.

use std::rc::Rc;
use once_self_cell::sync_once_self_cell;

struct Owner;

struct Borrowed<'a> {
    owner: &'a Owner,
    evil: Evil,
}

impl<'a> From<&'a Owner> for Borrowed<'a> {
    fn from(owner: &'a Owner) -> Borrowed<'a> {
        Borrowed {
            owner,
            evil: Evil::default(),
        }
    }
}

thread_local! {
    static STR: Rc<str> = "hello".into();
}

#[derive(Clone)]
struct Evil {
    rc: Rc<str>,
}

impl Drop for Evil {
    fn drop(&mut self) {
        let ptr = self.rc.as_ptr();
        let thread_id = std::thread::current().id();
        println!("drop rc at {:p} in thread {:?}", ptr, thread_id);
    }
}

impl Default for Evil {
    fn default() -> Evil {
        Evil {
            rc: STR.with(|x| x.clone()),
        }
    }
}

sync_once_self_cell!(
    SelfRef,
    Owner,
    Borrowed<'_>,
);

fn main() {
    let cell = SelfRef::new(Owner);
    let _evil1 = cell.get_or_init_dependent().evil.clone();
    std::thread::spawn(move || {
        let _evil2 = cell.get_or_init_dependent().evil.clone();
    });
}
drop rc at 0x21b53dbd0d0 in thread ThreadId(1)
drop rc at 0x21b53dbd0d0 in thread ThreadId(2)
drop rc at 0x21b53dbd0d0 in thread ThreadId(2)

Double-free with `into_owner`

use std::cell::Cell;

use self_cell::self_cell;

type O = Cell<Option<Box<u8>>>;

self_cell! {
    struct S {
        owner: O,
        #[covariant]
        dependent: D,
    }
}

struct D<'a>(&'a O);

impl Drop for D<'_> {
    fn drop(&mut self) {
        self.0.take();
    }
}

fn main() {
    let s = S::new(Cell::new(Some(Box::new(42))), |o| D(o));
    s.into_owner();
}
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/small_pg`
thread 'main' panicked at 'explicit panic', src/main.rs:20:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
free(): double free detected in tcache 2
[1]    394203 IOT instruction (core dumped)  cargo r

The underlying problem is

let owner_ptr: *const Owner = &(*joined_ptr.as_ptr()).owner;
// Move owner out so it can be returned.
let owner = read(owner_ptr);
// Clean up rest of JoinedCell
drop_in_place(&mut (*joined_ptr.as_ptr()).dependent);

where the owner is read before dependent is dropped, so dropping dependent can access the original owner, but into_owner returns the copy created beforehand.


I have a branch that fixes this as well as some memory leaks: ebbf355...c482860

The branch is based on #21 (one of the memory leaks is in drop_joined), so I’ll only open a PR after #21 is merged. So consider not to release a new version yet after merging #21 😉

Covariance in owner is problematic / can lead to unsoundness

use self_cell::self_cell;

type X<'a> = &'a str;

self_cell! {
    pub struct Foo<'a> {
        owner: fn(&'a ()),
        #[covariant]
        dependent: X,
    }
}

fn transmute_lifetime<'a, 'b>(x: &'a str) -> &'b str {
    fn helper<'b>(s: &'b str) -> impl for<'a> FnOnce(&'a fn(&'b ())) -> &'a str {
        move |_| s
    }
    let x: Foo<'a> = Foo::new(|_| (), helper(x));
    let x: Foo<'static> = x; // coerce using variance 
    let y = Box::leak(Box::new(x));
    y.borrow_dependent()
}

fn main() {
    let r;
    {
        let s = "Hello World".to_owned();
        r = transmute_lifetime(s.as_str());
        dbg!(r); // "Hello World"
    }
    dbg!(r); // prints garbage :-)
}

comparison with selfref

selfref requires more recent rustc but supports more lifetime complexity akin to that of ouroboros, as well as no-alloc environments.

thoughts?

Unsound cyclic re-borrow

I’ll try to explain (and compare to ouroboros) tomorrow

use std::cell::RefCell;

use self_cell::self_cell;

struct Bar<'a>(RefCell<(Option<&'a Bar<'a>>, String)>);

self_cell! {
    struct Foo {
        owner: (),
        #[not_covariant]
        dependent: Bar,
    }
}

fn main() {
    let mut x = Foo::new((), |_| Bar(RefCell::new((None, "Hello".to_owned()))));
    x.with_dependent(|_, dep| {
        dep.0.borrow_mut().0 = Some(dep);
    });
    x.with_dependent_mut(|_, dep| {
        let r1 = dep.0.get_mut();
        let string_ref_1 = &mut r1.1;
        let mut r2 = r1.0.unwrap().0.borrow_mut();
        let string_ref_2 = &mut r2.1;

        let s = &string_ref_1[..];
        string_ref_2.clear();
        string_ref_2.shrink_to_fit();
        println!("{}", s); // prints garbage
    });
}

Provide proper doc.rs documentation

As it stands no doc comments nor further documentation has been done. Visiting the doc.rs page for this project should give good helpful information about the interface.

UB for borrowed type containing interior mutability

Filing to track @jDomantas's report in https://www.reddit.com/r/rust/comments/m42fjx/safetouse_procmacrofree_selfreferential_structs/gqt6jkm/.

The signature of get_or_init_dependent improperly permits the caller to pick an arbitrarily short lifetime 'a, and then put a reference that is only live for 'a into Dependent. Then it's possible for them to later pick a longer lifetime 'b and get a Dependent<'b> back out, leading to Use After Free or other undefined behavior in safe code.

use once_self_cell::sync_once_self_cell;
use std::cell::Cell;

struct Owner(Cell<&'static str>);

struct Borrowed<'a>(Cell<&'a str>);

impl<'a> From<&'a Owner> for Borrowed<'a> {
    fn from(owner: &'a Owner) -> Borrowed<'a> {
        let r = owner.0.get();
        Borrowed(Cell::new(r))
    }
}

sync_once_self_cell!(
    SelfRef,
    Owner,
    Borrowed<'_>,
);

fn do_evil(cell: &SelfRef) {
    let str = String::from("short lived");
    let dep = cell.get_or_init_dependent();
    dep.0.set(&str);
}

fn main() {
    let cell = SelfRef::new(Owner(Cell::new("static string")));
    do_evil(&cell);
    let dep = cell.get_or_init_dependent();
    println!("string: {:?}", dep.0.get());
}

self_cell rejects cases where dependent is a path containing "::"

Minimal example:

self_cell::self_cell!(
    struct Foo {
        owner: Vec<u8>,

        #[covariant]
        dependent: nested::Bar,
    }
);

mod nested {
    pub struct Bar<'a> {
        v: &'a u8,
    }    
}

Fails with:

error: no rules expected the token `::`
   --> src/lib.rs:6:26
    |
6   |         dependent: nested::Bar,
    |                          ^^ no rules expected this token in macro call
    |
note: while trying to match `,`
   --> /Users/alex_gaynor/.cargo/registry/src/github.com-1ecc6299db9ec823/self_cell-0.10.2/src/lib.rs:322:36
    |
322 |         dependent: $Dependent:ident,
    |                                    ^

error: could not compile `qq` due to previous error

Perhaps replacing ident with path there would be sufficient?

warning: missing documentation for an associated function

async_imap crate maintained at https://github.com/async-email/async-imap/ has #![warn(missing_docs)] enabled. When self_cell is used as in async-email/async-imap#86, CI fails with warnings like

warning: missing documentation for an associated function
  --> src/types/name.rs:7:1
   |
7  | / self_cell!(
8  | |     /// A name that matches a `LIST` or `LSUB` command.
9  | |     pub struct Name {
10 | |         owner: Box<ResponseData>,
...  |
16 | |     impl { Debug }
17 | | );
   | |_^
   |
note: the lint level is defined here
  --> src/lib.rs:76:9
   |
76 | #![warn(missing_docs)]
   |         ^^^^^^^^^^^^
   = note: this warning originates in the macro `self_cell` (in Nightly builds, run with -Z macro-backtrace for more info)

A solution could be to add docs to generated methods or at least #[allow(missing_docs)] attributes.

Cannot use #[from_fn] since I update to 0.9.0

Hi

I have this code and using self_cell = "0.8.0"

self_cell!(
    struct MyStruct1 {
        #[from_fn]
        owner: MyStruct2,

        #[covariant]
        dependent: MyEnum,
    }
);

Since I updated to 0.9.0 , I have, on #[from_fn] the error message :

no rules expected the token #

no rules expected this token in macro callrustc
myfile.rs(21, 9): no rules expected this token in macro call
no rules expected the token #
[Note] no rules expected this token in macro call

I'm still a newbie on rust and I don't find the way to change that.

Covariance check with `_assert_covariance` is insufficient, unsound

use std::{cell::RefCell, fmt};

use self_cell::self_cell;

self_cell! {
    struct WrongVarianceExample {
        owner: (),
        #[covariant]
        dependent: Dependent,
    }
}

// this type is not covariant
type Dependent<'a> = RefCell<Box<dyn fmt::Display + 'a>>;

Dependent<'a> is not covariant in 'a, however unsize coercion still allows an OWNED value

RefCell<Box<dyn fmt::Display + 'x>>

to be coerced into

RefCell<Box<dyn fmt::Display + 'y>>

for lifetimes 'x: 'y, hence _assert_covariance compiles fine.

Unsoundness follows.

fn main() {
    let cell = WrongVarianceExample::new((), |_| RefCell::new(Box::new("")));
    let s = String::from("Hello World");

    // borrow_dependent unsound due to incorrectly checked variance
    *cell.borrow_dependent().borrow_mut() = Box::new(s.as_str());

    // s still exists
    cell.with_dependent(|_, d| println!("{}", d.borrow()));

    drop(s);

    // s is gone
    cell.with_dependent(|_, d| println!("{}", d.borrow()));
}

(run in Rust Explorer)

Hello World
p����U���

A better _assert_covariance needs to check whether coercing the Dependents behind a level of indirection is still possible.

Options include coercing Box<Dependent<'x>> to Box<Dependent<'y>>, or (more close to the actual use-case) coercing &'y Dependent<'x> into &'y Dependent<'y>.

Performance diff vs. ouroboros

In Fluent we use ouroboros right now and I crated a draft PR to switch to once_self_cell because we are aiming to minimize the dependency chain - projectfluent/fluent-rs#221

At the moment iai indicates that there is a perf loss in our use case with that PR:

iai_resolve_preferences
  Instructions:             1880367 (+0.723568%)
  L1 Accesses:              2681940 (+0.894907%)
  L2 Accesses:                 9870 (+0.162371%)
  RAM Accesses:                6532 (+0.415065%)
  Estimated Cycles:         2959910 (+0.845389%)

It's not a large diff, but I'm wondering if the cost is inevitable or does it indicate an opportunity for an optimization.

Support try_from

First: Thank you for making this crate - it's really easy to use and has helped me make several of my own APIs simpler.

I have some code that reads a JSON file into a String then parses that JSON into a Thing<'de> using #[serde(borrow)] to avoid thousands/millions of string allocations. I'd like to use sync_once_self_cell with it, but I'm not sure how I'd deal with the possibility of the deserialization producing an error.

Ideally, I could do something like this:

struct Thing<'de>(Vec<Item<'de>>);

impl<'de> TryFrom<&'de String> for Thing<'de> {
    type Error = serde_json::Error;

    fn try_from(s: &'de String) -> Result<Self, Self::Error> {
        serde_json::from_str(s).map(Self)
    }
}

sync_try_once_self_cell(ThingStore, String, Thing<'_>);

fn use_it(s: String) -> Result<ThingStore, serde_json::Error> {
    ThingStore::new(s)
}

Alternatives/Workarounds Considered

  • Call unwrap in a From<&'de String> for Thing<'de> impl. Sadly, the data I'm working with contains errors I can't fix, so this would always crash my program.
  • Change Thing<'de> to be Thing<'de>(Result<Vec<Item<'de>>, serde_json::Error>) - it seems inefficient to have to check if it's Ok every time I go to use it.

Hygienic bug in macro (Ok function)

Similar to #16, but this time self_cell doesn't allow a custom Ok function:

use self_cell::self_cell;

// Remove this, and it compiles
pub fn Ok<T>(t: T) -> Result<T, ()> {
  Result::Ok(t)
}

#[derive(Debug, Eq, PartialEq)]
struct Ast<'a>(pub Vec<&'a str>);

self_cell!(
    struct AstCell {
        owner: String,

        #[covariant]
        dependent: Ast,
    }

    impl {Debug, Eq, PartialEq}
);

fn build_ast_cell(code: &str) -> AstCell {
  // Create owning String on stack.
  let pre_processed_code = code.trim().to_string();

  // Move String into AstCell, then build Ast inplace.
  AstCell::new(pre_processed_code, |code| {
    Ast(code.split(' ').filter(|word| word.len() > 1).collect())
  })
}

fn main() {
  let ast_cell = build_ast_cell("fox = cat + dog");

  println!("ast_cell -> {:?}", &ast_cell);
  println!("ast_cell.borrow_owner() -> {:?}", ast_cell.borrow_owner());
  println!(
    "ast_cell.borrow_dependent().0[1] -> {:?}",
    ast_cell.borrow_dependent().0[1]
  );
}

Compile error:

error[E0308]: mismatched types
  --> svc-gateway-host-run/src/e.rs:11:1
   |
11 | / self_cell!(
12 | |     struct AstCell {
13 | |         owner: String,
14 | |
...  |
19 | |     impl {Debug, Eq, PartialEq}
20 | | );
   | | ^
   | | |
   | | expected `Result<AstCell, Err>`, found `Result<AstCell, ()>`
   | |_expected this type parameter
   |   expected `Result<AstCell, Err>` because of return type
   |
   = note: expected enum `Result<_, Err>`
              found enum `Result<_, ()>`
   = note: this error originates in the macro `self_cell` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0308]: mismatched types
  --> svc-gateway-host-run/src/e.rs:11:1
   |
11 | / self_cell!(
12 | |     struct AstCell {
13 | |         owner: String,
14 | |
...  |
19 | |     impl {Debug, Eq, PartialEq}
20 | | );
   | | ^
   | | |
   | |_expected `Result<AstCell, (..., ...)>`, found `Result<AstCell, ()>`
   |   expected `Result<AstCell, (String, Err)>` because of return type
   |
   = note: expected enum `Result<_, (String, Err)>`
              found enum `Result<_, ()>`
   = note: this error originates in the macro `self_cell` (in Nightly builds, run with -Z macro-backtrace for more info)

Context: was trying out anyhow's Ok helper

Eagerly-initialized variant?

This crate is wonderful, thank you! I've got a use case that'd benefit from a variant that's eagerly initialized, instead of lazily.

The reason is that my code for generating the dependant needs another parameter, in addition to owner. This means that From::from, which can be called anywhere doesn't work. Instead I'd benefit from an API that let's you do:

AstCell::new(body, |body| blah blah)

this way the closure could close over the other paramater.

Does this seem reasonable?

Use of addr_of_mut requires recent Rust

I got a pull request sent for the new yank which I very much appreciate (mitsuhiko/minijinja#21). However the latest version uses addr_of_mut! which bumps up the rustc requirement to 1.51. I was previously able to target 1.42 which I need for an upstream user of this crate.

Would it be possible to restore support for 1.42 somehow?

Safe API for mapping between types whose owner is Arc<T>

A frequent use case I have is multiple self_cell types which all have an owner of Arc<T>, and I'd like to be able to "map" between them. Here's an example of this:

use std::sync::Arc;

struct X(());
struct T1<'a>(&'a ());
#[derive(Debug)]
struct T2<'a>(&'a ());

self_cell::self_cell! {
    struct O1 {
        owner: Arc<X>,
        #[covariant]
        dependent: T1,
    }
}

self_cell::self_cell! {
    struct O2 {
        owner: Arc<X>,
        #[covariant]
        dependent: T2,
    }
}

unsafe fn relifetime<'a, 'b>(t: &'a T1<'a>) -> &'b T1<'b> {
    std::mem::transmute(t)
}

fn map(o: &O1, f: impl for<'this> FnOnce(&'this X, &T1<'this>) -> T2<'this>) -> O2 {
    O2::new(Arc::clone(&o.borrow_owner()), |x| {
        f(&x, unsafe { relifetime(o.borrow_dependent()) })
    })
}

fn main() {
    let o1 = O1::new(Arc::new(X(())), |x| T1(&x.0));
    let o2 = map(&o1, |_, t1| T2(t1.0));
    drop(o1);

    o2.with_dependent(|_, v| println!("{:?}", v));
}

Given an O1, I'd like to be able to safely create an O2 which points at the same data.

The implementation of map here works, but it'd be great if there was some way this functionality could be generalized as a part of self-cell, so consumers didn't need to carry unsafe code.

Is it sound to add `borrow_dependent_mut`?

Hey,

I'm new to this crate and to the problem it's trying to solve, but I've been using this crate for a bit and it seems to work fine. I'll trust you on the safety part 🙂.

The with_dependent_mut function is a bit tedious to use though. Would it be possible to add a function like borrow_dependent_mut? Intuitively a function with this signature feels safe to me:

fn borrow_dependent_mut<'a>(&'a mut self) -> &'a mut Session<'a>

Is there a reason why this function cannot be safely implemented?

Convert self_cell struct back into owner

Hi,
rental has a way to convert a struct back into the owner (I think it’s calling into_head()). This can be used to e.g. reuse a Vec<u8> after the self-referencing struct is not needed anymore.
Could self_cell add something similar?
I hope implementing something like impl From<$StructName> for $Owner {} is safe to do.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.