Editing
Rust
(section)
Jump to navigation
Jump to search
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
=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. It's a barrier to how programs normally evolve over time. Here's an example of a context that has a random number generator. I was wanting something similar for a Genetic programming experiment, except with many more functions and a struct that owned a struct that owned struct, which was generic meaning all the ones above had to be as well. Context->Population->Entity->Genome<SomeGenomeType> Context holds something like the following: Population<EntityType>, SelectionStrategy<EntityType?>, MutationFunction<SomeGenomeType>, FitnessFunction<SomeGenomeType> Most of those require references back to context (for access to the rng, population and so on). I'm sure the basic architecture must not be idiomatic Rust and some kind of flat approach would be better. Another example would be passing in a Reader/Writer/Logger to some App context. ==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. No one will have the option of using their quantum hardware random number generator now. 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 is simple and allows for generics to be avoided. But it requires dynamic dispatch adding overhead. When Boxing a member variable in a struct, the struct's constructor must be updated to take a box and any setter methods. Also the at the constructor call site must now Box it's value. Users/consumers/owners of the struct however are unaffected. 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== Theoretically the better solution for most cases. But it requires lifetime to be specified in generics. This ripples throughout the program. %99 of the time you just want to use the same lifetime parameters for all uses but rust forces them to be specified... every.. single.. time... extern crate rand; use rand::{Rng, StdRng}; pub struct Context<'a> { // <- This must be changed rng: &'a Rng, // <- So this can be changed } impl<'a> Context<'a> { // The same generic signature must be added here... twice... pub fn new(rng: &Rng) -> Context<'a> { // <- ...Here Context {rng: rng} } pub fn set_rng(&self, rng: &Rng) { self.rng = rng; } } impl<'a> SomeTrait for Context<'a> { // <- ...twice again!... //...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) } Consider the extra complexity of switching from a Box<> to a &' borrow. Now you have to also revert the changes in the Box example. Also what happens if you have multiple different member variable references, maybe mixing both boxes and borrows... And structs that own structs that themselves are genetic... ==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 unaffected. But any structs that want to hold a reference the struct (Now a trait) must now deal with the same trait object problem (unless they are fine with fixing the implementation). pub trait IsContext { pub fn set_rng(&self, rng: &Rng); pub fn evolve(&self); } pub struct ContextRaw<'a> { // Still deal with generics here... rng: &'a Rng<'a>, // ...here } impl IsContext for ContextRaw<'a> { pub fn set_rng(&self, rng: &Rng) { self.rng = rng; } pub fn evolve(&self) { //... } } ==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 must be initialised in a thread safe way. Testing and changing defaults is a bit of extra work but can be done via monkey patching. Globals suck. For example rusts simple log class or the stdout() global. ==Using closures== Pass in the function of the object you want to call, rather than the object. More work at the construct site and seems to defeat the point of having traits. Not good if what your passing in has lots of functions that are used.
Summary:
Please note that all contributions to Hegemon Wiki may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
Hegemon Wiki:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Navigation menu
Personal tools
Not logged in
Talk
Contributions
Log in
Namespaces
Page
Discussion
English
Views
Read
Edit
Edit source
View history
More
Search
Navigation
Main page
Recent changes
Random page
Help about MediaWiki
Tools
What links here
Related changes
Special pages
Page information