2 releases

0.1.4 Mar 1, 2020
0.1.3 Feb 28, 2020
0.1.2 Feb 27, 2020
0.1.1 Feb 26, 2020
0.1.0 Feb 25, 2020

#2029 in Data structures

MIT license

19KB
172 lines

GSRS or Generic Self Referencing Struct

Allows to create almost arbitrary self referencing struct

See documentation on docs.rs for more details

Licence

MIT


lib.rs:

GSRS or Generic Self Referencing Struct

This crate helps to create custom movable self referencing structs. Nothing magical. It just wraps Owner and references to it in single package with simple but unsafe lifetime tricks.

Self referencing structs are generally considered an anti-pattern in Rust, so if you can easily go without it you should do it. But sometimes you actually need to have a self referential struct. So here are some examples when you actually need SRS:

  • If you have structure that is built on references (graph with Arena, or any structure built with slices on top of the string) and you want to be able move it to another thread, or put it into Vec.
  • If your api would be much better if you will be able to return self contained values.

Does not support dependent lifetimes (yet?, is it actully needed/possible?)

Should work on any stable rust starting from 1.31(2018 edition)

Usage

Simple example:

use gsrs::*;
struct Test{field:usize}
#[derive(Default)]
struct TestRef<'a>(Option<&'a Test>);
deref_with_lifetime!(TestRef);
// create owned part
let mut srs = SRS::<Test, TestRef>::new( Test{ field: 5 } );
// create self-referencing part
srs.with(|user, owner|*user = TestRef(Some(owner)));
// get self referencing part back
let r = srs.get_ref(|user, _| user.0.unwrap());
println!("{}", r.field);

or you can do creation in one go. Although you anyway have to specify type of the referencing part because type inference is getting confused by changed lifetime. This can be fixed, but only when GATs will be stabilized.

use gsrs::*;
struct Test{field:usize}
struct TestRef<'a>(&'a Test);
deref_with_lifetime!(TestRef);
// create owned part and self-referencing part
let mut srs = SRS::<_, TestRef>::create_with(
    Test{ field: 5 },
    |owner|TestRef(owner)
);
// get self referencing part back
let r = srs.get_ref(|user, _| user.0);
println!("{}", r.field);

Referencing part can be arbitrary complex:

use gsrs::*;
struct TestRef<'a>(Vec<&'a str>);
deref_with_lifetime!(TestRef);
// create owned part and self-referencing part
let mut srs = SRS::<_, TestRef>::create_with(
    "long unicode string".to_owned(),
    |owner|TestRef(owner.split(' ').collect())
);
// get self referencing part back
let r = srs.get_ref(|user, _| user.0[1]);
assert_eq!("unicode", r);

and this won't compile because get_ref is able to supply return value with proper lifetime:

srs.with(|user, owner|*user = TestRef(Some(owner)));
let r = srs.get_ref(|user, _| user.0.unwrap());
drop(srs);
println!("{}",r.field);

This also will fail because it is possible to return only static types or references to static types. It is done to prevent changing some inner reference with interior mutability.

let mut srs = SRS::<Test,TestRef<'static>>::create_with(
    Test{field:5},
    |owner|TestRef(owner),
);
// here closure returns TestRef<'a> not a reference
let r = srs.with(|user,_|user);
let mut ow = Box::new(Test{field:0});
let r = srs.split(&mut ow);
println!("{}",r.0.field);

No runtime deps