You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
What if we had another trait Bar that only needed x and y? Should we make Foo inherit Bar to save code? Or, perhaps we should just use Foo even if we don't need z? The larger libraries you write, such problems tends to get hairier and uglier to deal with.
This problem can be solved with something called "duck typing". Instead of creating large traits with many methods, you try to make them as small as possible.
There is a problem with this: What if we can set an x value, but not get one? We could split the traits into GetX, SetX etc.
Yet, there is another problem: Some people use a get_mut/get pattern. get_mut only works when there is an interior pointer to the type. For example, it won't work if you need to convert an i32 to i64.
Then, there is another problem: Some people like the builder pattern:
let x = Foo::new(...).bar(1.0).baz(2.0);
Then, there is yet another problem: In generic code, people might want to put a shared object inside a struct with &RefCell<T> or Rc<RefCell<T>>, but they also might want it work with an owned object:
This can lead to inconsistent API design where one library might not work with another because it uses different smart pointers. Sometimes we want more control in performance sensitive code to pick a smart pointer implementation, but when this is not case, we just want to have a nice get/set pattern.
Let's write up all the problems so far:
Shared properties between traits
Incompatible get/set patterns
Builder pattern
Smart pointers in generic code
When using Piston-Quack, you define a get/set pattern by using GetFrom and SetAt.
When using the library, we can import the Get and Set traits:
use quack::Set;usePosition;
foo.set(Position([1.0,2.0]));
This also has the advantage that the imported type Position is the same as how you call the method.
More, it simplifies process when designing a new API:
Add structs and enums for the data types
Add GetFrom/SetAt/ActOn impls
Done!
Now, if we want to extend the API with new methods, we could make the traits inherit the properties!
Here is an example:
pubtraitRelativeTransform:Clonewhere(Transform,Self):Pair<Data = Transform,Object = Self> + GetFrom + SetAt{/// Appends transform to the current one.#[inline(always)]fnappend_transform(&self,transform:Matrix2d) -> Self{letTransform(mat) = self.get();self.clone().set(Transform(multiply(mat, transform)))}// a bunch of other methods
...
}
The Pair constraint is necessary at the moment to make Rust understand the tuple. This might disappear when Rust gets full equality constraints.
Now we can auto implement the trait for all types that has these properties:
Assume we have the following trait with a property pattern:
What if we had another trait
Bar
that only neededx
andy
? Should we makeFoo
inheritBar
to save code? Or, perhaps we should just useFoo
even if we don't needz
? The larger libraries you write, such problems tends to get hairier and uglier to deal with.This problem can be solved with something called "duck typing". Instead of creating large traits with many methods, you try to make them as small as possible.
Now, you can make
Foo
inheritX
:There is a problem with this: What if we can set an x value, but not get one? We could split the traits into
GetX
,SetX
etc.Yet, there is another problem: Some people use a
get_mut/get
pattern.get_mut
only works when there is an interior pointer to the type. For example, it won't work if you need to convert ani32
toi64
.Then, there is another problem: Some people like the builder pattern:
Then, there is yet another problem: In generic code, people might want to put a shared object inside a struct with
&RefCell<T>
orRc<RefCell<T>>
, but they also might want it work with an owned object:This can lead to inconsistent API design where one library might not work with another because it uses different smart pointers. Sometimes we want more control in performance sensitive code to pick a smart pointer implementation, but when this is not case, we just want to have a nice get/set pattern.
Let's write up all the problems so far:
When using Piston-Quack, you define a get/set pattern by using
GetFrom
andSetAt
.This can be shortend down by using the
quack!
macro:When using the library, we can import the
Get
andSet
traits:This also has the advantage that the imported type
Position
is the same as how you call the method.More, it simplifies process when designing a new API:
GetFrom/SetAt/ActOn
implsNow, if we want to extend the API with new methods, we could make the traits inherit the properties!
Here is an example:
The
Pair
constraint is necessary at the moment to make Rust understand the tuple. This might disappear when Rust gets full equality constraints.Now we can auto implement the trait for all types that has these properties:
The text was updated successfully, but these errors were encountered: