Rust: Difference between revisions

From Hegemon Wiki
Jump to navigation Jump to search
Line 74: Line 74:
Requires lifetime to be specified in generics.
Requires lifetime to be specified in generics.


extern crate rand;
<code>
use rand::{Rng, StdRng};
extern crate rand;
pub struct Context<'a> { // <- This must be changed
use rand::{Rng, StdRng};
pub struct Context<'a> { // <- This must be changed
rng: &'a Rng, // <- So this can be changed
}
rng: &'a Rng, // <- So this can be changed
impl Context<'a> { // The same generic signature must be added here
}
pub fn new(rng: &Rng) -> Context<'a> { // <- ...Here
impl Context<'a> { // The same generic signature must be added here
pub fn new(rng: &Rng) -> Context<'a> { // <- ...Here
Context {rng: rng}
}
Context {rng: rng}
pub fn set_rng(&self, rng: &Rng) {
}
pub fn set_rng(&self, rng: &Rng) {
self.rng = rng;
}
self.rng = rng;
}
}
impl SomeTrait for Context<'a> { // <- ...Here
}
impl SomeTrait for Context<'a> { // <- ...Here
//...Blah
}
//...Blah
fn use_context(ctx: &Context<'a>) { // <- ...and here
}
//...
fn use_context(ctx: &Context<'a>) { // <- ...and here
}
//...
fn main() {
}
let seed: &[_] = &[1, 2, 3, 4];
fn main() {
let seed: &[_] = &[1, 2, 3, 4];
let mut rng = StdRng::from_seed(seed);
let mut rng = StdRng::from_seed(seed);
let mut ctx = Context::new(&rng);
use_context(&ctx)
let mut ctx = Context::new(&rng);
}
use_context(&ctx)
}
</code>
</code>



Revision as of 03:52, 10 December 2016

Trait Objects

Swapping between different ownership/reference types in Rust is a huge pain point. The generics can ripple throughout a program making maintenance/refactoring a big hassle.

Consider some context that has a random number generator. I was wanting something similar for a Genetic programming experiment:

Without Traits

This suffers from fixing the Rng to a specific implementation and prevents dependency inversion causing problems with things like testing and forcing any downstream users to be stuck with the choice.

extern crate rand;
use rand::StdRng;
pub struct Context {
   rng: StdRng,
}
impl Context {
   pub fn new() -> Context {
       let seed: &[_] = &[1, 2, 3, 4];
       let mut rng = StdRng::from_seed(seed);
   }
   pub fn set_rng(&self, rng: StdRng) {
       self.rng = rng;
   }
}
impl SomeTrait for Context {
    //...Blah
    fn some_function(self) {}
}
fn use_context(ctx: &Context) {
    //...Blah
}
fn main() {
   let mut ctx = Context::new();
   use_context(&ctx)
}

Using Box's

Using boxes allows for generics to be avoided.

When turning something into a Box on the struct, the struct's constructor must be updated, any mutator functions and the at the constructor call site. Users/consumers/owners of the struct however are unaffected.

This requires dynamic dispatch adding overhead.

extern crate rand;
use rand::{Rng, StdRng};
pub struct Context {
   rng: Box<Rng>, // <- This is changed
}
impl Context {
   pub fn new(rng: Box<Rng>) -> Context {  // <- Now this line must be changed
       Context {rng: rng}
   }
   pub fn set_rng(&self, rng: Box<Rng>) {  // <- ...this line must be changed
       self.rng = rng;
   }
}
impl SomeTrait for Context {
    //...Blah
}
fn use_context(ctx: &Context) {
    //...
}
fn main() {
   let seed: &[_] = &[1, 2, 3, 4];
   let mut rng = StdRng::from_seed(seed);
   let mut ctx = Context::new(Box::new(rng)); // <- ...this line must be changed
   use_context(&ctx)
}

Using &'s

Requires lifetime to be specified in generics.

extern crate rand;
use rand::{Rng, StdRng};
pub struct Context<'a> { // <- This must be changed
   rng: &'a Rng, // <- So this can be changed
}
impl Context<'a> { // The same generic signature must be added here
   pub fn new(rng: &Rng) -> Context<'a> {  // <- ...Here
       Context {rng: rng}
   }
   pub fn set_rng(&self, rng: &Rng) {
       self.rng = rng;
   }
}
impl SomeTrait for Context<'a> { // <- ...Here
    //...Blah
}
fn use_context(ctx: &Context<'a>) { // <- ...and here
    //...
}
fn main() {
   let seed: &[_] = &[1, 2, 3, 4];
   let mut rng = StdRng::from_seed(seed);
   let mut ctx = Context::new(&rng);
   use_context(&ctx)
}

Using trait's instead of the struct

Requires traits to be defined. Still needs generics on the structs functions such as the constructor. However user/consumers functions are uneffected. Other structs that own this struct must now deal with the same trait object problem.

pub struct ContextRaw {
}

Using a global

Doesn't allow for multiple different contexts with their own separate Rng generators. Maybe some thread_local variant would work but that seems to be a pain and a hack. Globals suck.