I'm playing with rasterization and ray tracing, and my algorithm works on objects that can be rotated, translated, scaled, and converted to a bunch of triangles. I implement this by creating the IntoTriangle trait:
pub trait IntoTriangle {
fn triangulate(&self) -> Vec<Triangle>;
fn rotate(&mut self, x: f32, y: f32, z: f32);
fn scale(&mut self, x: f32, y: f32, z: f32);
fn translate(&mut self, x: f32, y: f32, z: f32);
}
And then in my program I transform the object before converting it into triangles:
let obj = MyObjectType::new(...);
obj.scale(7.0, 7.0, 7.0);
obj.rotate(0.0, 45.0, 0.0);
obj.translate(3.0, 0.0, -30.0);
let triangles = obj.triangulate();
This code has been working for a long time. But recently I was going through a piece of unrelated code from gtk-rs and noticed how they stack up such calls in a very nice way, which I really loved because the usage becomes so much clearer:
let triangles = MyObjectType::new(...)
.scale(7.0, 7.0, 7.0)
.rotate(0.0, 45.0, 0.0)
.translate(3.0, 0.0, -30.0)
.triangulate();
To achieve this I would need to change scale/rotate/translate functions to take mut self and return Self (this is inspired by the gtk-rs code: https://gtk-rs.org/gtk4-rs/stable/latest/docs/src/gtk4/auto/application_window.rs.html#428-431 ):
fn rotate(mut self, x: f32, y: f32, z: f32) -> Self {
self.rotation = [x, y, z];
self
}
But this is not object-safe and the compiler won't let me do this unless I move these functions out of the trait.
Is there any other way of achieving similar behaviour for trait objects? I'm at the point where I'm almost ready to move these methods out of the trait for the sake of retaining this gtk-inspired look and feel.
You could wrap everything in a builder over generic IntoTriangle objects:
struct Triangle {}
trait IntoTriangle {
fn triangulate(&self) -> Vec<Triangle>;
fn rotate(&mut self, x: f32, y: f32, z: f32);
fn scale(&mut self, x: f32, y: f32, z: f32);
fn translate(&mut self, x: f32, y: f32, z: f32);
}
#[derive(Default)]
struct Fake {}
impl IntoTriangle for Fake {
fn triangulate(&self) -> Vec<Triangle> {
vec![]
}
fn rotate(&mut self, x: f32, y: f32, z: f32) {}
fn scale(&mut self, x: f32, y: f32, z: f32) {}
fn translate(&mut self, x: f32, y: f32, z: f32) {}
}
struct TriangleBuilder<T> {
inner: T,
}
impl<T: IntoTriangle + Default> TriangleBuilder<T> {
pub fn new() -> Self {
Self {
inner: Default::default(),
}
}
pub fn build(self) -> Vec<Triangle> {
self.inner.triangulate()
}
pub fn rotate(mut self, x: f32, y: f32, z: f32) -> Self {
self.inner.rotate(x, y, z);
self
}
pub fn scale(mut self, x: f32, y: f32, z: f32) -> Self {
self.inner.scale(x, y, z);
self
}
pub fn translate(mut self, x: f32, y: f32, z: f32) -> Self {
self.inner.translate(x, y, z);
self
}
}
fn main() {
let triangles = TriangleBuilder::<Fake>::new()
.rotate(0f32, 0f32, 0f32)
.scale(1f32, 1f32, 1f32)
.translate(3f32, 4f32, 5f32)
.build();
}
Playground
Note that I use a Default bind for the implementation (for simplicity). But if needed you could have a constructor that takes the object itself or a reference if you needed.
Related
Is it possible to have one line assignments in structs as an example:
pub struct Pipe {
texture: Texture2D,
x: f32,
y: f32,
}
Instead have something like
pub struct Pipe {
texture: Texture2D,
(x, y): (f32, f32)
}
Also an approach like this is not what I want, because I want to access the variable like Pipe.pos.x or Pipe.x instead of pipe.pos.0:
pub struct Pipe {
texture: Texture2D,
pos: (f32, f32)
}
Is it possible to have one line assignments in structs as an example:
No.
Also an approach like this is not what I want, because I want to access the variable like Pipe.pos.x or Pipe.x instead of pipe.pos.0:
Define a struct. And possibly a ctor function for convenience.
That's called tuple
pub struct Pipe {
texture: Texture2D,
pos: (f32, f32),
}
let pipe = Pipe {
texture: new_texture,
pos: (10.5, 24.0),
};
println!("x position is {}", pipe.pos.0);
println!("y position is {}", pipe.pos.1);
// to access using x and y you can create a trait
trait XYAccessor {
fn x(&self) -> f32
fn y(&self) -> f32
}
// implement trait for the tuple
impl XYAccessor for (f32,f32) {
fn x(&self) -> f32 {
self.0
}
fn y(&self) -> f32 {
self.1
}
}
// then you can use .x() and .y()
println!("x position is {}", pipe.pos.x());
println!("y position is {}", pipe.pos.y());
I am still quite new to the advanced topics in rust, but for context, I am trying to implement a generic quadtree in rust.
With the method find_mut(&mut self,x,y) I want to traverse the quadtree to find the lowermost subtree containing that coordinate and return a mutable reference of it.
Quadtree Struct
pub struct QuadTree<T> {
x: i32,
y: i32,
dx: i32,
dy: i32,
leaf: bool,
subtrees: [Option<Box<Self>>; 4],
value: Option<T>,
}
Methods and functions
fn find_mut(&mut self, x: i32, y: i32) -> Result<&mut Self, &mut Self> {
let mut current = self;
loop {
// If we've arrived at a leaf return it as Ok
if current.leaf { // First borrow occurs here for some reason
return Ok(current);
}
// Getting the subtree that contains our coordinate
match current.mut_subtree_at(x, y) {
// Go a level deeper
Some(child) => current = child,
// Return an Err containing the lowest subtree that is sadly not a leaf
None => return Err(current),
}
}
}
fn subtree_id<'a>(&'a self, x: i32, y: i32) -> usize {
let mut child_id = 0;
if x >= self.x {
child_id += 1;
}
if y >= self.y {
child_id += 2;
}
child_id
}
#[inline(always)]
fn mut_subtree_at(&mut self, x: i32, y: i32) -> Option<&mut Self> {
self.subtrees[self.subtree_id(x, y)].as_deref_mut()
}
Error
error[E0499]: cannot borrow `*current` as mutable more than once at a time
--> src/quadtree.rs:128:36
|
115 | fn find_mut(&mut self, x: i32, y: i32) -> Result<&mut Self, &mut Self> {
| - let's call the lifetime of this reference `'1`
...
121 | return Ok(current);
| ----------- returning this value requires that `*current` is borrowed for `'1`
...
124 | match current.mut_subtree_at(x, y) {
| ---------------------------- first mutable borrow occurs here
...
128 | None => return Err(current),
| ^^^^^^^ second mutable borrow occurs here
How would you approach this problem. I am missing something about the way borrowing mutable references and lifetimes work?
While not ideal, here is a recursive version that compiles, but requires querying mut_subtree_at twice:
pub struct QuadTree<T> {
x: i32,
y: i32,
dx: i32,
dy: i32,
leaf: bool,
subtrees: [Option<Box<Self>>; 4],
value: Option<T>,
}
impl<T> QuadTree<T> {
fn find_mut(&mut self, x: i32, y: i32) -> Result<&mut Self, &mut Self> {
if self.leaf {
Ok(self)
} else {
if self.mut_subtree_at(x, y).is_some() {
// Go a level deeper
self.mut_subtree_at(x, y).unwrap().find_mut(x, y)
} else {
// Return an Err containing the lowest subtree that is sadly not a leaf
Err(self)
}
}
}
fn subtree_id<'a>(&'a self, x: i32, y: i32) -> usize {
let mut child_id = 0;
if x >= self.x {
child_id += 1;
}
if y >= self.y {
child_id += 2;
}
child_id
}
#[inline(always)]
fn mut_subtree_at(&mut self, x: i32, y: i32) -> Option<&mut Self> {
self.subtrees[self.subtree_id(x, y)].as_deref_mut()
}
}
To my understanding this works because .is_some() returns a bool, which is an owned value and therefore Rust can prove that at the time of Err(self) no reference is held to self any more.
It seems that the same principle also works for the iterative solution:
pub struct QuadTree<T> {
x: i32,
y: i32,
dx: i32,
dy: i32,
leaf: bool,
subtrees: [Option<Box<Self>>; 4],
value: Option<T>,
}
impl<T> QuadTree<T> {
fn find_mut(&mut self, x: i32, y: i32) -> Result<&mut Self, &mut Self> {
let mut current = self;
loop {
// If we've arrived at a leaf return it as Ok
if current.leaf {
return Ok(current);
}
// Getting the subtree that contains our coordinate
if current.mut_subtree_at(x, y).is_some() {
// Go a level deeper
current = current.mut_subtree_at(x, y).unwrap()
} else {
// Return an Err containing the lowest subtree that is sadly not a leaf
return Err(current);
}
}
}
fn subtree_id<'a>(&'a self, x: i32, y: i32) -> usize {
let mut child_id = 0;
if x >= self.x {
child_id += 1;
}
if y >= self.y {
child_id += 2;
}
child_id
}
#[inline(always)]
fn mut_subtree_at(&mut self, x: i32, y: i32) -> Option<&mut Self> {
self.subtrees[self.subtree_id(x, y)].as_deref_mut()
}
}
For more performance (meaning, if you don't want to call mut_subtree_at twice), you would have to override the borrow checker as this is obviously a false positive.
Luckily, there is the polonius-the-crab crate that is written for specifically the problem you ran into and hides the unsafe code in a safe and reliable manner.
Here is my first working version using polonius-the-crab:
use ::polonius_the_crab::prelude::*;
use polonius_the_crab::WithLifetime;
pub struct QuadTree<T> {
x: i32,
y: i32,
dx: i32,
dy: i32,
leaf: bool,
subtrees: [Option<Box<Self>>; 4],
value: Option<T>,
}
impl<T> QuadTree<T> {
fn find_mut(&mut self, x: i32, y: i32) -> Result<&mut Self, &mut Self> {
type SelfRef<K> = dyn for<'lt> WithLifetime<'lt, T = &'lt mut QuadTree<K>>;
let mut current = self;
loop {
// If we've arrived at a leaf return it as Ok
if current.leaf {
return Ok(current);
}
// Getting the subtree that contains our coordinate
match polonius::<SelfRef<T>, _, _, _>(current, |current| {
current.mut_subtree_at(x, y).ok_or(())
}) {
Ok(child) => {
// Go a level deeper
current = child;
}
Err((current, ())) => {
// Return an Err containing the lowest subtree that is sadly not a leaf
return Err(current);
}
}
}
}
fn subtree_id<'a>(&'a self, x: i32, y: i32) -> usize {
let mut child_id = 0;
if x >= self.x {
child_id += 1;
}
if y >= self.y {
child_id += 2;
}
child_id
}
#[inline(always)]
fn mut_subtree_at(&mut self, x: i32, y: i32) -> Option<&mut Self> {
self.subtrees[self.subtree_id(x, y)].as_deref_mut()
}
}
or this version, which is a little easier to understand as it uses polonius' macros:
use ::polonius_the_crab::prelude::*;
pub struct QuadTree<T> {
x: i32,
y: i32,
dx: i32,
dy: i32,
leaf: bool,
subtrees: [Option<Box<Self>>; 4],
value: Option<T>,
}
impl<T> QuadTree<T> {
pub fn find_mut(&mut self, x: i32, y: i32) -> Result<&mut Self, &mut Self> {
let mut current = self;
while !current.leaf {
// Getting the subtree that contains our coordinate.
// If no subtree exists, return Err(current).
current = current.mut_subtree_at(x, y)?;
}
// If we are at a leaf node with the coordinate, success!
Ok(current)
}
fn subtree_id<'a>(&'a self, x: i32, y: i32) -> usize {
let mut child_id = 0;
if x >= self.x {
child_id += 1;
}
if y >= self.y {
child_id += 2;
}
child_id
}
#[inline(always)]
fn mut_subtree_at(&mut self, x: i32, y: i32) -> Result<&mut Self, &mut Self> {
let mut current = self;
polonius!(
|current| -> Result<&'polonius mut Self, &'polonius mut Self> {
if let Some(child) = current.subtrees[current.subtree_id(x, y)].as_deref_mut() {
polonius_return!(Ok(child))
}
}
);
// Return the ownership of `self` back through the `Err()` value.
// This in conjunction with the `polonius!()` macro resolves the
// ownership problem.
Err(current)
}
}
This yields E0119:
#![feature(destructuring_assignment)]
pub struct Point<T> {
pub x: T,
pub y: T,
}
impl<F, T: From<F>> From<Point<F>> for Point<T> {
fn from<F>(f: Point<F>) -> Self {
let Point { mut x, mut y } = f;
(x, y) = (T::from(x), T::from(y));
Self { x, y }
}
}
The implementation conflict is in std::convert::From for T:
impl<T> From<T> for T {
fn from(t: T) -> T {
t
}
}
I assume there isn't a way to work around this so much as there may be more rust-idiomatic approach to the problem of converting between inner types, or even a more idiomatic understanding of what the "problem" is.
You can implement it as a normal method that takes a closure and then pass it the From::from trait method when you call it. Or even specialize it for types that implement From<T>:
#[derive(Debug)]
pub struct Point<T> {
pub x: T,
pub y: T,
}
impl<T> Point<T> {
pub fn map<U>(self, mut f: impl FnMut(T) -> U) -> Point<U> {
let Point { x, y } = self;
Point {
x: f(x),
y: f(y),
}
}
pub fn map_into<U: From<T>>(self) -> Point<U> {
let Point { x, y } = self;
Point {
x: U::from(x),
y: U::from(y),
}
}
}
fn main() {
let point_i16 = Point { x: 6i16, y: 3200i16 };
let point_f32: Point<f32> = point_i16.map(From::from);
let point_f64: Point<f64> = point_f32.map_into();
println!("{:?}", point_f64); // Point { x: 6.0, y: 3200.0 }
}
I'm trying to implement trait IDimTransformer for different generic types of traits IDimOffset0Transformer and IDimOffset1Transformer but compiler give me error
trait IDimTransformer {
fn get_offset_x(&self, x: i32) -> i32;
fn get_offset_z(&self, z: i32) -> i32;
}
trait IDimOffset0Transformer : IDimTransformer {}
trait IDimOffset1Transformer : IDimTransformer {}
impl<T: IDimOffset0Transformer> IDimTransformer for T {
fn get_offset_x(&self, x: i32) -> i32 {
return x;
}
fn get_offset_z(&self, z: i32) -> i32 {
return z;
}
}
impl<T: IDimOffset1Transformer> IDimTransformer for T {
fn get_offset_x(&self, x: i32) -> i32 {
return x - 1;
}
fn get_offset_z(&self, z: i32) -> i32 {
return z - 1;
}
}
Example of use
struct Layer {}
impl IDimOffset1Transformer for Layer{}
fn some_func(dim: &impl IDimTransformer, x: i32, z: i32) -> i32 {
return dim.get_offset_x(x) + dim.get_offset_z(x);
}
fn some_func_1(layer: &Layer, x: i32, z: i32) -> i32 {
return some_func(layer, x, z);
}
Compiler error
error[E0119]: conflicting implementations of trait `layer::IDimTransformer`:
--> src/layer.rs:59:1
|
49 | impl<T: IDimOffset0Transformer> IDimTransformer for T {
| ----------------------------------------------------- first implementation here
...
59 | impl<T: IDimOffset1Transformer> IDimTransformer for T {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
I think the pattern you are looking for looks like this in rust:
trait Transformer {
fn get_offset_x(x: i32) -> i32;
fn get_offset_z(z: i32) -> i32;
}
struct Offset0Transformer;
impl Transformer for Offset0Transformer {
fn get_offset_x(x: i32) -> i32 {
x
}
fn get_offset_z(z: i32) -> i32 {
z
}
}
struct Offset1Transformer;
impl Transformer for Offset1Transformer {
fn get_offset_x(x: i32) -> i32 {
x - 1
}
fn get_offset_z(z: i32) -> i32 {
z - 1
}
}
struct Layer { x: i32, z: i32 };
impl Layer {
fn add_transformed<T: Transformer>(&self) -> i32 {
T::get_offset_x(self.x) + T::get_offset_z(self.z)
}
}
fn main() {
let layer = Layer { x: 5, z: 2 };
let result = layer.add_transformed::<Offset1Transformer>();
println!("got: {}", result);
}
Most of the time you won't need this though. Thinking through what your code is trying to do and thinking of a simpler way usually gets you smaller better code.
I'm learning Rust so this might be a duplicate, since I'm still not sure how to search this. I tried to make a enum that contains different structs and since those structs have same methods but different implementation, I can't figure out how to properly write the types for the trait and the implementation. This is what I have so far:
struct Vector2 {
x: f32,
y: f32,
}
struct Vector3 {
x: f32,
y: f32,
z: f32,
}
enum Vector {
Vector2(Vector2),
Vector3(Vector3),
}
trait VectorAdd {
fn add(&self, other: &Vector) -> Vector;
}
impl VectorAdd for Vector2 {
fn add(&self, other: &Vector2) -> Vector2 {
Vector2 {
x: self.x + other.x,
y: self.y + other.y
}
}
}
This code does not compile, and the error messages don't make it clearer for me. Anyone can guide me how to write this properly? or if it's even possible?
Since you are using generics here, you don't need the enum to write the trait:
struct Vector2 {
x: f32,
y: f32,
}
struct Vector3 {
x: f32,
y: f32,
z: f32,
}
trait VectorAdd {
fn add(&self, other: &Self) -> Self;
}
impl VectorAdd for Vector2 {
fn add(&self, other: &Vector2) -> Vector2 {
Vector2 {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
impl VectorAdd for Vector3 {
fn add(&self, other: &Vector3) -> Vector3 {
Vector3 {
x: self.x + other.x,
y: self.y + other.y,
z: self.z + other.z,
}
}
}
(playground)
You can implement the enum based on this definition:
enum Vector {
Vector2(Vector2),
Vector3(Vector3),
}
impl VectorAdd for Vector {
fn add(&self, other: &Vector) -> Vector {
match (self, other) {
(Self::Vector2(a), Self::Vector2(b)) => Self::Vector2(a.add(b)),
(Self::Vector3(a), Self::Vector3(b)) => Self::Vector3(a.add(b)),
_ => panic!("invalid operands to Vector::add"),
}
}
}
Macros may help you if the number of variants gets large.