Polymorphism alternatives in rust - rust

I have 2 very similar structs and I wanted to ask if there is something similar like polymorphism in java/kotlin in rust?
struct Player {
jump_power: f32,
color: Color,
size: (f32, f32),
pos: (f32, f32),
}
struct Pipe {
color: Color,
speed: f32,
size: (f32, f32),
pos: (f32, f32),
}
I'm not sure where exactly I should start.

Rust often requires a different approach to fit more neatly into what Rust has in the way of capabilities. If you want something that can be either a Pipe or a Player then you probably want an enum:
enum Actor {
Player(Player),
Pipe(Pipe)
}
Where now that Actor can be one of either of those things.
Where you have common properties like color you can even do:
impl Actor {
pub fn color(&self) -> Color {
match self {
Self::Player(p) => p.color,
Self::Pipe(p) => p.color
}
}
}
Often you'll want to impl From<Player> for Actor as a convenience when constructing these, but that's not the only way to do it.

Here is a way to do it with traits and generics:
#[derive(Debug)]
struct Color {
rgb: [u8; 3],
}
trait ColorType {
fn color(&self) -> &Color;
}
struct Player {
jump_power: f32,
color: Color,
size: (f32, f32),
pos: (f32, f32),
}
impl ColorType for Player {
fn color(&self) -> &Color {
&self.color
}
}
struct Pipe {
color: Color,
speed: f32,
size: (f32, f32),
pos: (f32, f32),
}
impl ColorType for Pipe {
fn color(&self) -> &Color {
&self.color
}
}
fn get_color<T: ColorType>(my_color_type: &T) -> &Color {
&my_color_type.color()
}
fn main() {
let pipe = Pipe {
color: Color { rgb: [255u8, 255u8, 255u8] },
speed: 0.0,
size: (0.0, 0.0),
pos: (0.0, 0.0),
};
let player = Player {
jump_power: 0.0,
color: Color { rgb: [255u8, 255u8, 255u8] },
size: (0.0, 0.0),
pos: (0.0, 0.0),
};
println!("Pipe color: {:#?}", get_color(&pipe));
println!("Player color: {:#?}", get_color(&player));
}
output:
Pipe color: Color {
rgb: [
255,
255,
255,
],
}
Player color: Color {
rgb: [
255,
255,
255,
],
}
Rust Playground Link

Related

Using serde with trait objects

I am trying to use serde to serialize trait objects. I had a look at
How to implement `serde::Serialize` for a boxed trait object?
How can we write a generic function for checking Serde serialization and deserialization?
https://github.com/dtolnay/erased-serde
but I do not understand how to put the pieces together. Please show me how to proceed with this.
This code mimics the example in The Rust Programming Language:
use serde::Serialize; // 1.0.126
trait Paint: Serialize {
fn paint(&self) -> String;
}
#[derive(Serialize)]
struct Pen {
color: String,
}
#[derive(Serialize)]
struct Brush {
color: String,
}
impl Paint for Pen {
fn paint(&self) -> String {
return format!("Pen painting with color {}", self.color);
}
}
impl Paint for Brush {
fn paint(&self) -> String {
return format!("Brush painting with color {}", self.color);
}
}
#[derive(Serialize)]
struct Canvas {
height: f32,
width: f32,
tools: Vec<Box<dyn Paint>>,
}
impl Paint for Canvas {
fn paint(&self) -> String {
let mut s = String::new();
for tool in &self.tools {
s.push_str("\n");
s.push_str(&tool.paint());
}
return s;
}
}
fn main() {
let pen = Pen {
color: "red".to_owned(),
};
let brush = Brush {
color: "blue".to_owned(),
};
let canvas = Canvas {
height: 12.0,
width: 10.0,
tools: vec![Box::new(pen), Box::new(brush)],
};
println!("{}", canvas.paint());
serde_json::to_string(&canvas).unwrap();
}
The code does not compile due to the object-safety rules:
error[E0038]: the trait `Paint` cannot be made into an object
--> src/main.rs:33:12
|
33 | tools: Vec<Box<dyn Paint>>,
| ^^^^^^^^^^^^^^^^^^^ `Paint` cannot be made into an object
|
= help: consider moving `serialize` to another trait
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.126/src/ser/mod.rs:247:8
|
247 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
| ^^^^^^^^^ ...because method `serialize` has generic type parameters
|
::: src/main.rs:3:7
|
3 | trait Paint: Serialize {
| ----- this trait cannot be made into an object...
I understand that trait objects cannot have methods with generic parameters, but in my case I only need to serialize the Canvas struct to JSON. Is there something I could do to make that work?
The ideal serialized output would be
{
"Canvas": {
"height": 12.0,
"width": 10.0,
"tools": {
"Pen": {
"color": "red"
},
"Brush": {
"color": "blue"
}
}
}
}

Rust: allow multiple threads to modify an image (wrapper of a vector)?

Suppose I have a "image" struct that wraps a vector:
type Color = [f64; 3];
pub struct RawImage
{
data: Vec<Color>,
width: u32,
height: u32,
}
impl RawImage
{
pub fn new(width: u32, height: u32) -> Self
{
Self {
data: vec![[0.0, 0.0, 0.0]; (width * height) as usize],
width: width,
height: height
}
}
fn xy2index(&self, x: u32, y: u32) -> usize
{
(y * self.width + x) as usize
}
}
It is accessible through a "view" struct, which abstracts an inner block of the image. Let's assume that I only want to write to the image (set_pixel()).
pub struct RawImageView<'a>
{
img: &'a mut RawImage,
offset_x: u32,
offset_y: u32,
width: u32,
height: u32,
}
impl<'a> RawImageView<'a>
{
pub fn new(img: &'a mut RawImage, x0: u32, y0: u32, width: u32, height: u32) -> Self
{
Self{ img: img,
offset_x: x0, offset_y: y0,
width: width, height: height, }
}
pub fn set_pixel(&mut self, x: u32, y: u32, color: Color)
{
let index = self.img.xy2index(x + self.offset_x, y + self.offset_y);
self.img.data[index] = color;
}
}
Now suppose I have an image, and I want to have 2 threads modifying it at the same time. Here I use rayon's scoped thread pool:
fn modify(img: &mut RawImageView)
{
// Do some heavy calculation and write to the image.
img.set_pixel(0, 0, [0.1, 0.2, 0.3]);
}
fn main()
{
let mut img = RawImage::new(20, 10);
let pool = rayon::ThreadPoolBuilder::new().num_threads(2).build().unwrap();
pool.scope(|s| {
let mut v1 = RawImageView::new(&mut img, 0, 0, 10, 10);
let mut v2 = RawImageView::new(&mut img, 10, 0, 10, 10);
s.spawn(|_| {
modify(&mut v1);
});
s.spawn(|_| {
modify(&mut v2);
});
});
}
This doesn't work, because
I have 2 &mut img at the same time, which is not allowed
"closure may outlive the current function, but it borrows v1, which is owned by the current function"
So my questions are
How can I modify RawImageView, so that I can have 2 threads modifying my image?
Why does it still complain about life time of the closure, even though the threads are scoped? And how do I overcome that?
Playground link
One approach that I tried (and it worked) was to have modify() just create and return a RawImage, and let the thread push it into a vector. After all the threads were done, I constructed the full image from that vector. I'm trying to avoid this approach due to its RAM usage.
Your two questions are actually unrelated.
First the #2 that is easier:
The idea of the Rayon scoped threads is that the threads created inside cannot outlive the scope, so any variable created outside the scope can be safely borrowed and its references sent into the threads. But your variables are created inside the scope, and that buys you nothing.
The solution is easy: move the variables out of the scope:
let mut v1 = RawImageView::new(&mut img, 0, 0, 10, 10);
let mut v2 = RawImageView::new(&mut img, 10, 0, 10, 10);
pool.scope(|s| {
s.spawn(|_| {
modify(&mut v1);
});
s.spawn(|_| {
modify(&mut v2);
});
});
The #1 is trickier, and you have to go unsafe (or find a crate that does it for you but I found none). My idea is to store a raw pointer instead of a vector and then use std::ptr::write to write the pixels. If you do it carefully and add your own bounds checks it should be perfectly safe.
I'll add an additional level of indirection, probably you could do it with just two but this will keep more of your original code.
The RawImage could be something like:
pub struct RawImage<'a>
{
_pd: PhantomData<&'a mut Color>,
data: *mut Color,
width: u32,
height: u32,
}
impl<'a> RawImage<'a>
{
pub fn new(data: &'a mut [Color], width: u32, height: u32) -> Self
{
Self {
_pd: PhantomData,
data: data.as_mut_ptr(),
width: width,
height: height
}
}
}
And then build the image keeping the pixels outside:
let mut pixels = vec![[0.0, 0.0, 0.0]; (20 * 10) as usize];
let mut img = RawImage::new(&mut pixels, 20, 10);
Now the RawImageView can keep a non-mutable reference to the RawImage:
pub struct RawImageView<'a>
{
img: &'a RawImage<'a>,
offset_x: u32,
offset_y: u32,
width: u32,
height: u32,
}
And use ptr::write to write the pixels:
pub fn set_pixel(&mut self, x: u32, y: u32, color: Color)
{
let index = self.img.xy2index(x + self.offset_x, y + self.offset_y);
//TODO! missing check bounds
unsafe { self.img.data.add(index).write(color) };
}
But do not forget to either do check bounds here or mark this function as unsafe, sending the responsibility to the user.
Naturally, since your function keeps a reference to a mutable pointer, it cannot be send between threads. But we know better:
unsafe impl Send for RawImageView<'_> {}
And that's it! Playground. I think this solution is memory-safe, as long as you add code to enforce that your views do not overlap and that you do not go out of bounds of each view.
This does not exactly match your image problem but this might give you some clues.
The idea is that chunks_mut() considers a whole mutable slice as many independent (non-overlapping) mutable sub-slices.
Thus, each mutable sub-slice can be used by a thread without considering that the whole slice is mutably borrowed by many threads (it is actually, but in a non-overlapping manner, so it is sound).
Of course this example is trivial, and it should be trickier to divide an image in many arbitrary non-overlapping areas.
fn modify(
id: usize,
values: &mut [usize],
) {
for v in values.iter_mut() {
*v += 1000 * (id + 1);
}
}
fn main() {
let mut values: Vec<_> = (0..8_usize).map(|i| i + 1).collect();
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(2)
.build()
.unwrap();
pool.scope(|s| {
for (id, ch) in values.chunks_mut(4).enumerate() {
s.spawn(move |_| {
modify(id, ch);
});
}
});
println!("{:?}", values);
}
Edit
I don't know the context in which this parallel work on some parts of an image is needed but I can imagine two situations.
If the intent is to work on some arbitrary parts of the image and allow this to take place in several threads that compute many other things, then a simple mutex to regulate the access to the global image is certainly enough.
Indeed, if the precise shape of each part of the image is very important, then it is very unlikely that there exist so many of them that parallelisation can be beneficial.
On the other hand, if the intent is to parallelize the image processing in order to achieve high performance, then the specific shape of each part is probably not so relevant, since the only important guaranty to ensure is that the whole image is processed when all the threads are done.
In this case a simple 1-D splitting (along y) is enough.
As an example, below is a minimal adaptation of the original code in order to make the image split itself into several mutable parts that can be safely handled by many threads.
No unsafe code is needed, no expensive runtime checks nor copies are performed, the parts are contiguous so highly optimisable.
type Color = [f64; 3];
pub struct RawImage {
data: Vec<Color>,
width: u32,
height: u32,
}
impl RawImage {
pub fn new(
width: u32,
height: u32,
) -> Self {
Self {
data: vec![[0.0, 0.0, 0.0]; (width * height) as usize],
width: width,
height: height,
}
}
fn xy2index(
&self,
x: u32,
y: u32,
) -> usize {
(y * self.width + x) as usize
}
pub fn mut_parts(
&mut self,
count: u32,
) -> impl Iterator<Item = RawImagePart> {
let part_height = (self.height + count - 1) / count;
let sz = part_height * self.width;
let width = self.width;
let mut offset_y = 0;
self.data.chunks_mut(sz as usize).map(move |part| {
let height = part.len() as u32 / width;
let p = RawImagePart {
part,
offset_y,
width,
height,
};
offset_y += height;
p
})
}
}
pub struct RawImagePart<'a> {
part: &'a mut [Color],
offset_y: u32,
width: u32,
height: u32,
}
impl<'a> RawImagePart<'a> {
pub fn set_pixel(
&mut self,
x: u32,
y: u32,
color: Color,
) {
let part_index = x + y * self.width;
self.part[part_index as usize] = color;
}
}
fn modify(img: &mut RawImagePart) {
// Do some heavy calculation and write to the image.
let dummy = img.offset_y as f64 / 100.0;
let last = img.height - 1;
for x in 0..img.width {
img.set_pixel(x, 0, [dummy + 0.1, dummy + 0.2, dummy + 0.3]);
img.set_pixel(x, last, [dummy + 0.7, dummy + 0.8, dummy + 0.9]);
}
}
fn main() {
let mut img = RawImage::new(20, 10);
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(2)
.build()
.unwrap();
pool.scope(|s| {
for mut p in img.mut_parts(2) {
s.spawn(move |_| {
modify(&mut p);
});
}
});
for y in 0..img.height {
let offset = (y * img.width) as usize;
println!("{:.2?}...", &img.data[offset..offset + 3]);
}
}

Do Rust builder patterns have to use redundant struct code?

I was looking at the Method syntax section of the Rust documentation and came across an example of the builder pattern. The CircleBuilder struct in the example below is an exact duplicate of the Circle struct. It seems like this redundant code violates the usual norms of programming.
I understand why the example created a new struct, because the creator did not want to implement the builder methods against the original Circle struct. That is fine, but is there a way to rewrite this example so that there is no redundancy--yet still keeping the nice builder interface in the main() function intact?
I tried to create an empty struct or a struct with just one throwaway element, but that did not work.
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
struct CircleBuilder {
x: f64,
y: f64,
radius: f64,
}
impl CircleBuilder {
fn new() -> CircleBuilder {
CircleBuilder { x: 0.0, y: 0.0, radius: 1.0, }
}
fn x(&mut self, coordinate: f64) -> &mut CircleBuilder {
self.x = coordinate;
self
}
fn y(&mut self, coordinate: f64) -> &mut CircleBuilder {
self.y = coordinate;
self
}
fn radius(&mut self, radius: f64) -> &mut CircleBuilder {
self.radius = radius;
self
}
fn finalize(&self) -> Circle {
Circle { x: self.x, y: self.y, radius: self.radius }
}
}
fn main() {
let c = CircleBuilder::new()
.x(1.0)
.y(2.0)
.radius(2.0)
.finalize();
println!("area: {}", c.area());
println!("x: {}", c.x);
println!("y: {}", c.y);
}
Do Rust builder patterns have to use redundant struct code?
No. But sometimes they might. For example, consider if we wanted to have special logic (or even just complicated logic) around our constructor:
/// Width must always be greater than height!
struct HorizontalEllipse {
width: f64,
height: f64,
}
impl HorizontalEllipse {
fn area(&self) -> f64 {
std::f64::consts::PI * (self.width / 2.0) * (self.height / 2.0)
}
}
struct HorizontalEllipseBuilder {
width: f64,
height: f64,
}
impl HorizontalEllipseBuilder {
fn new() -> HorizontalEllipseBuilder {
HorizontalEllipseBuilder {
width: 0.0,
height: 0.0,
}
}
fn width(&mut self, width: f64) -> &mut HorizontalEllipseBuilder {
self.width = width;
self
}
fn height(&mut self, height: f64) -> &mut HorizontalEllipseBuilder {
self.height = height;
self
}
fn finalize(&self) -> Result<HorizontalEllipse, String> {
let HorizontalEllipseBuilder { height, width } = *self;
if height >= width {
Err("This is not horizontal".into())
} else {
Ok(HorizontalEllipse { width, height })
}
}
}
fn main() {
let c = HorizontalEllipseBuilder::new()
.width(1.0)
.height(2.0)
.finalize()
.expect("not a valid ellipse");
println!("area: {}", c.area());
println!("width: {}", c.width);
println!("height: {}", c.height);
}
Now a HorizontalEllipse knows that it is always true that width > height. We've moved that check from many potential places (each method) to one, the constructor. We then moved the constructor to a new type because it was complicated (not really, but truly complicated examples are usually... complicated).
Many builders I've seen also have "enhanced" types of the real object:
#[derive(Debug)]
struct Person {
name: String,
}
#[derive(Debug, Default)]
struct PersonBuilder {
name: Option<String>,
}
impl PersonBuilder {
fn name(self, name: &str) -> Self {
PersonBuilder { name: Some(name.into()), ..self }
}
fn build(self) -> Person {
Person {
name: self.name.unwrap_or_else(|| "Stefani Joanne Angelina Germanotta".into()),
}
}
}
fn main() {
let person = PersonBuilder::default().build();
println!("{:?}", person);
let person = PersonBuilder::default().name("krishnab").build();
println!("{:?}", person);
}
You don't see that in the book's example because it's trying to be simpler and not involve ownership concerns.
This seems like the sort of thing a macro might be able to do. A quick search found the derive_builder and builder_macro crates which seem to implement this functionality.

Mix lifetimes of references in Vec

I am attempting to generate a Vec<&'b Color> from Vec<&'a Color>:
impl <'a> Canvas<'a> {
pub fn modify<'b>(&self, update: Update) -> Canvas<'b> {
let x = update.x;
let y = update.y;
let colors: Vec<&'b Color> = self.colors.iter().enumerate().map(move |(index, color)| {
if index == self.width * y + x { update.color } else { color }
})
.collect();
Canvas { width: self.width, height: self.height, colors: colors }
}
}
However, I get the following error:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/canvas.rs:51:50
|
51 | let colors: Vec<&'b Color> = self.colors.iter().enumerate().map(move |(index, color)| {
| ^^^^
How can I create a Vec of colors, all but one with lifetime 'a, and the remaining with lifetime 'b?
If needed, the relevant definitions are as follows:
#[derive(Debug, PartialEq, Eq)]
pub enum Color {
White,
Red,
Blue,
Green,
Purple,
Orange
}
pub struct Update<'a> {
color: &'a Color,
x: usize,
y: usize
}
pub struct Canvas<'a> {
width: usize,
height: usize,
colors: Vec<&'a Color>
}
How can I create a Vec of colors, all but one with lifetime 'a, and the remaining with lifetime 'b?
You cannot. Lifetimes are generics, and just like it doesn't make sense to have "a Vec<T>, all but one of T is the type String, and the remaining T is the type bool", it doesn't make sense to do so with lifetimes.
What you can do is unify the lifetimes:
impl<'a> Canvas<'a> {
pub fn modify(&self, update: Update<'a>) -> Canvas<'a> {
let x = update.x;
let y = update.y;
let colors = self.colors
.iter()
.enumerate()
.map(move |(index, color)| if index == self.width * y + x {
update.color
} else {
color
})
.collect();
Canvas {
width: self.width,
height: self.height,
colors: colors,
}
}
}
Here, we've said that the update.color lifetime and the lifetime of the contained references have an intersection, and that's what the lifetime of the result will be.
Another common solution for a heterogenous collection of a fixed set of types is to introduce an enum with variants for each one:
pub enum Thing<'a, 'b> {
A(&'a Color),
B(&'b Color),
}
impl<'a> Canvas<'a> {
pub fn modify<'b>(&self, update: Update<'b>) -> Vec<Thing<'a, 'b>> {
let x = update.x;
let y = update.y;
self.colors
.iter()
.enumerate()
.map(move |(index, color)| if index == self.width * y + x {
Thing::B(update.color)
} else {
Thing::A(color)
})
.collect()
}
}

Unable to have an ImageBuffer in a struct — two arguments needed and only provided one

I am new to Rust and still learning. I am a bit confused as to why when I compile this code it fails. I am building it with Cargo on my Linux development machine
extern crate image;
extern crate rand;
use image::{ImageBuffer, Rgb};
use rand::Rng;
struct ImageCanvas {
image: image::ImageBuffer,
WIDTH: i32,
HEIGHT: i32,
xPosition: i32,
yPosition: i32
}
impl ImageCanvas {
pub fn new(width: i32, height: i32) -> ImageCanvas {
// create a new instance of an ImagePen.
ImageCanvas{
image: ImageBuffer::<Rgb<u8>>::new(width, height),
WIDTH: width,
HEIGHT: height,
xPosition: 0,
yPosition: 0
}
}
pub fn paint(&self, x: i32, y: i32, rgb: i32){
self.xPosition = x;
self.yPosition = y;
//RGB should be an array.
self.image.get_pixel_mut(self.xPosition, self.yPosition).data = rgb;
}
}
fn main() {
let canvas = ImageCanvas::new(50, 50);
canvas.paint(1, 1, [rand::thread_rng().gen_range(1, 255),
rand::thread_rng().gen_range(1, 255),
rand::thread_rng().gen_range(1, 255)]);
canvas.image.save("yay.png").unwrap();
}
The error:
src/main.rs:9:9: 9:27 error: wrong number of type arguments: expected 2, found 0 [E0243]
src/main.rs:9 image: image::ImageBuffer,
^~~~~~~~~~~~~~~~~~
src/main.rs:9:9: 9:27 help: run `rustc --explain E0243` to see a detailed explanation
error: aborting due to previous error
I am not sure why it is giving me this error. I have searched, but I haven't seen any other post about a noob like me not understanding the OOD of Rust.
image::ImageBuffer is a parametrized struct:
pub struct ImageBuffer<P: Pixel, Container> {
// some fields omitted
}
So you have to provide these parameters in your struct ImageCanvas.
something like
struct ImageCanvas<P: Pixel, Container> {
image: image::ImageBuffer<P, Container>,
WIDTH: i32,
HEIGHT: i32,
xPosition: i32,
yPosition: i32
}
or
struct ImageCanvas {
image: image::ImageBuffer<RGB<u8>, Vec<RGB<u8>>>,
WIDTH: i32,
HEIGHT: i32,
xPosition: i32,
yPosition: i32
}
You are actually using an RGB Image so why not used the specialized version
extern crate image;
extern crate rand;
use image::{RgbImage, Rgb};
use rand::Rng;
// Had to change all of the `i32` to `u32`. I agree, `i32` makes more sense.
struct ImageCanvas {
image: image::RgbImage, // image::ImageBuffer<Rgb<u8>>,
WIDTH: u32,
HEIGHT: u32,
xPosition: u32,
yPosition: u32
}
impl ImageCanvas {
pub fn new(width: u32, height: u32) -> ImageCanvas {
// create a new instance of an ImagePen.
ImageCanvas{
image: RgbImage::new(width, height), //ImageBuffer::<Rgb<u8>>::new(width, height),
WIDTH: width,
HEIGHT: height,
xPosition: 0,
yPosition: 0
}
}
// RGB _IS_ an array
// note the need for `mut`
pub fn paint(&mut self, x: u32, y: u32, rgb: [u8;3]){
self.xPosition = x;
self.yPosition = y;
self.image.get_pixel_mut(self.xPosition, self.yPosition).data = rgb;
}
}
fn main() {
// `mut` here too
let mut canvas = ImageCanvas::new(50, 50);
canvas.paint(1, 1, [rand::thread_rng().gen_range(1, 255),
rand::thread_rng().gen_range(1, 255),
rand::thread_rng().gen_range(1, 255)]);
canvas.image.save("yay.png").unwrap();
}

Resources