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()
}
}
Related
I can't understand how to write trait for generating Point in some range.
I try to use rand::Rng for generate random values for Point struct (for coordinates in minesweeper).
I use this site Generate Random Values - Rust Cookbook
And it fine worked for ranges for simple data types. Also, worked examples with Point.
Could someone provide example of code and some explanation about traits for Uniform for Point struct?
use rand::Rng;
use rand::distributions::{Distribution, Uniform};
fn main() {
let width: u8 = 15; let height: u8 = 15;
let mine_count: u8 = 40;
let mut rng = rand::thread_rng();
let ranger = Uniform::from(0..width);
for _i in 0..=mine_count{
let rand_point: Point = ranger.sample(&mut rng);
println!("Random Point: {:?}", rand_point);
}
}
#[derive(Debug)]
struct Point {
x: u8,
y: u8,
}
impl Distribution<Point> for Uniform<Point> {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
let (rand_x, rand_y) = rng.gen();
Point {
x: rand_x,
y: rand_y,
}
}
}
Compiling my-project v0.1.0 (/home/runner/SyntaxTest)
error[E0277]: the trait bound `Point: SampleUniform` is not satisfied
--> src/main.rs:56:30
|
56 | impl Distribution<Point> for Uniform<Point> {
| ^^^^^^^^^^^^^^ the trait `SampleUniform` is not implemented for `Point`
note: required by a bound in `Uniform`
--> /home/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/rand-0.8.5/src/distributions/uniform.rs:179:23
|
179 | pub struct Uniform<X: SampleUniform>(X::Sampler);
| ^^^^^^^^^^^^^ required by this bound in `Uniform`
error[E0277]: the trait bound `Point: SampleUniform` is not satisfied
--> src/main.rs:57:30
|
57 | fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
| ^^^^^ the trait `SampleUniform` is not implemented for `Point`
note: required by a bound in `Uniform`
--> /home/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/rand-0.8.5/src/distributions/uniform.rs:179:23
|
179 | pub struct Uniform<X: SampleUniform>(X::Sampler);
| ^^^^^^^^^^^^^ required by this bound in `Uniform`
The problem here is that your code has no concept of what is a distribution of Points.
If you look at the signature of the Uniform struct, its generic has to implement the SampleUniform trait. According to the docs, the SampleUniform trait defines the Sampler type that should be used in order to generate random distributions.
So, when you write this:
impl Distribution<Point> for Uniform<Point> {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
let (rand_x, rand_y) = rng.gen();
Point {
x: rand_x,
y: rand_y,
}
}
}
When you call Uniform::from(0..width), you are also defining a sampler for u8 values, which is not what you want in the end. What you want (judging by your code) is to generate a random distribution of points between a min, say Point { x: 0, y: 0 }, and a maximum value, which could be another point, but it could also be defined as a range, a delta, an area, etc. The idea is to be able to write something like this:
let ranger = Uniform::from(
Point::default()..Point {
x: width,
y: height,
},
);
Where Point::default() is the origin.
To solve your problem you need to be able to tell the rand crate what exactlyis it that you mean when you ask for that distribution of Points.
To keep your code as is, you could rewrite it like this:
use rand::distributions::uniform::{SampleUniform, UniformInt, UniformSampler};
use rand::distributions::{Distribution, Uniform};
use rand::Rng;
fn main() {
let width: u8 = 4;
let height: u8 = 5;
let mine_count: u8 = 40;
let mut rng = rand::thread_rng();
let ranger = Uniform::from(
Point::default()..Point {
x: width,
y: height,
},
);
for _i in 0..=mine_count {
let rand_point: Point = ranger.sample(&mut rng);
println!("Random Point: {:?}", rand_point);
}
}
#[derive(Clone, Copy, Debug, Default)]
struct Point {
x: u8,
y: u8,
}
struct UniformPoint {
x: UniformInt<u8>,
y: UniformInt<u8>,
}
impl SampleUniform for Point {
type Sampler = UniformPoint;
}
impl UniformSampler for UniformPoint {
type X = Point;
fn new<B1, B2>(low: B1, high: B2) -> Self
where
B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
{
let low = *low.borrow();
let high = *high.borrow();
UniformPoint {
x: UniformInt::<u8>::new_inclusive(low.x, high.x - 1),
y: UniformInt::<u8>::new_inclusive(low.y, high.y - 1),
}
}
fn new_inclusive<B1, B2>(low: B1, high: B2) -> Self
where
B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
{
let low = *low.borrow();
let high = *high.borrow();
UniformPoint {
x: UniformInt::<u8>::new_inclusive(low.x, high.x),
y: UniformInt::<u8>::new_inclusive(low.y, high.y),
}
}
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
Point {
x: self.x.sample(rng),
y: self.y.sample(rng),
}
}
}
impl Distribution<Point> for Point {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
let (rand_x, rand_y) = rng.gen();
Point {
x: rand_x,
y: rand_y,
}
}
}
Notice that you don't need to implement the Distribution trait any longer, since Uniform::from only requires that your struct implements SampleUniform.
The trick here is that each field in Point needs to be generated from a distribution of values. Since you can use UniformInt::<u8>... to generate those distributions, then all you need is an accessory struct that can as a placeholders for those distributions.
Notice that now, when you call ranger.sample() you're actually making two calls. One to x.sample and one to y.sample.
This results in a neat API, where you can still use the ranger as you did before without any modifications.
Another solution would be to keep your u8 distribution and create your random points by doing:
for _i in 0..=mine_count {
let rand_point = Point {
x: ranger.sample(&mut rng),
y: ranger.sample(&mut rng),
}
println!("Random Point: {:?}", rand_point);
}
This way you don't need to implement anything, but it is also less fun.
Hope this helps.
You want to implement Distribution<Point> for Uniform<u8>, not Uniform<Point>:
use rand::Rng;
use rand::distributions::{Distribution, Uniform};
fn main() {
let width: u8 = 15; let height: u8 = 15;
let mine_count: u8 = 40;
let mut rng = rand::thread_rng();
let ranger = Uniform::from(0..width);
for _i in 0..=mine_count{
let rand_point: Point = ranger.sample(&mut rng);
println!("Random Point: {:?}", rand_point);
}
}
#[derive(Debug)]
struct Point {
x: u8,
y: u8,
}
impl Distribution<Point> for Uniform<u8> {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
let (rand_x, rand_y) = rng.gen();
Point {
x: rand_x,
y: rand_y,
}
}
}
Playground
struct Point { x: f64, y: f64 }
struct Circle { center: Point, radius: f64 }
struct Square { lowerLeftCorner: Point, side: f64 }
trait ShapeVisitor {
fn visit_circle(&mut self, c: &Circle);
fn visit_square(&mut self, c: &Square);
}
trait Shape {
fn accept<V: ShapeVisitor>(&self, sv: &mut V);
}
impl Shape for Circle {
fn accept<V: ShapeVisitor>(&self, sv: &mut V) {
sv.visit_circle(self);
}
}
impl Shape for Square {
fn accept<V: ShapeVisitor>(&self, sv: &mut V) {
sv.visit_square(self);
}
}
So here's a method that computes the total area:
fn area(shapes: Vec<Box<dyn Shape>>) -> f64 {
struct AreaCalculator {
area: f64,
}
impl ShapeVisitor for AreaCalculator {
fn visit_circle(&mut self, c: &Circle) {
self.area += std::f64::consts::PI * c.radius * c.radius;
}
fn visit_square(&mut self, r: &Square) {
self.area += r.side * r.side;
}
}
let mut calculator = AreaCalculator { area: 0.0 };
for shape in shapes {
(*shape).accept(calculator);
}
calculator.area
}
And here's an example program
fn main() {
let mut shapes: Vec<Box<dyn Shape>> = Vec::new();
let p = Point { x: 0.0, y: 0.0 };
let circle = Circle { center: p, radius: 12.0 };
shapes.push(Box::new(circle));
let area = compute_area(shapes);
println!("Area {:?}", area);
}
Here's the error report:
error[E0038]: the trait `Shape` cannot be made into an object
--> visitor.rs:26:25
|
26 | fn compute_area(shapes: Vec<Box<dyn Shape>>) -> f64 {
| ^^^^^^^^^^^^^^^^^^^ `Shape` cannot be made into an object
|
= help: consider moving `accept` 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>
--> visitor.rs:11:8
|
10 | trait Shape {
| ----- this trait cannot be made into an object...
11 | fn accept<V: ShapeVisitor>(&self, sv: &mut V);
| ^^^^^^ ...because method `accept` has generic type parameters
error: aborting due to previous error
Is there a simple way to rectify it?
In order to be able to create a trait object, the trait must be object safe. But your Shape is not object safe - it has generic type parameters. Removing the type parameters solves the issue:
#[derive(Copy, Clone)]
struct Point {
x: f64,
y: f64,
}
struct Circle {
center: Point,
radius: f64,
}
struct Square {
lowerLeftCorner: Point,
side: f64,
}
trait ShapeVisitor {
fn visit_circle(&mut self, c: &Circle);
fn visit_square(&mut self, c: &Square);
}
trait Shape {
fn accept(&self, sv: &mut dyn ShapeVisitor);
}
impl Shape for Circle {
fn accept(&self, sv: &mut dyn ShapeVisitor) {
sv.visit_circle(self);
}
}
impl Shape for Square {
fn accept(&self, sv: &mut dyn ShapeVisitor) {
sv.visit_square(self);
}
}
fn area(shapes: Vec<Box<dyn Shape>>) -> f64 {
struct AreaCalculator {
area: f64,
}
impl ShapeVisitor for AreaCalculator {
fn visit_circle(&mut self, c: &Circle) {
self.area += std::f64::consts::PI * c.radius * c.radius;
}
fn visit_square(&mut self, r: &Square) {
self.area += r.side * r.side;
}
}
let mut calculator = AreaCalculator { area: 0.0 };
for shape in shapes {
shape.accept(&mut calculator);
}
calculator.area
}
fn main() {
let mut shapes: Vec<Box<dyn Shape>> = Vec::new();
let p = Point { x: 0.0, y: 0.0 };
let circle = Circle {
center: p,
radius: 1.0,
};
shapes.push(Box::new(circle));
shapes.push(Box::new(Square {
lowerLeftCorner: p,
side: 10.0,
}));
let area = area(shapes);
println!("Area {:?}", area);
}
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]);
}
}
I'm having trouble with this code. I have no idea why it errors.
It's supposed to take camera input and then put that inside of a buffer struct, contained inside the putter struct.
extern crate rscam;
// use std::fs::File;
// use std::io::prelude::*;
const WIDTH: usize = 1280;
const HEIGHT: usize = 720;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Pyxel {
r: u8,
g: u8,
b: u8,
}
impl Pyxel {
fn new() -> Pyxel {
Pyxel { r: 0, g: 0, b: 0 }
}
}
#[repr(transparent)]
struct Buffer {
pyxels: [[Pyxel; WIDTH]; HEIGHT],
}
// impl Buffer {
// fn new(s: [[Pyxel; WIDTH]; HEIGHT]) -> Buffer {
// Buffer {
// pyxels: s,
// }
// }
// }
struct Putter {
x: usize,
y: usize,
buffer: &'static mut Buffer,
}
impl Putter {
fn put_pyxel(&mut self, r: u8, g: u8, b: u8) {
if self.x >= WIDTH {
self.move_down();
}
let col = self.x;
let row = self.y;
self.buffer.pyxels[row][col] = Pyxel { r: r, g: g, b: b };
self.x += 1;
}
fn move_down(&mut self) {
self.y += 1;
}
fn new() -> Putter {
Putter {
x: 0,
y: 0,
buffer: &mut Buffer {
pyxels: [[Pyxel::new(); WIDTH]; HEIGHT],
},
}
}
}
fn main() {
let mut camera = rscam::new("/dev/video0").unwrap();
camera
.start(&rscam::Config {
interval: (1, 30),
resolution: (1280, 720),
format: b"RGB3",
..Default::default()
})
.unwrap();
let frame = camera.capture().unwrap();
let mut putter = Putter::new();
}
It errors:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:65:20
|
57 | buffer: &mut Buffer {
| __________________________^
58 | | pyxels: [[Pyxel::new(); WIDTH]; HEIGHT],
59 | | },
| |_____________^ temporary value does not live long enough
60 | }
61 | }
| - temporary value only lives until here
|
= note: borrowed value must be valid for the static lifetime..
How do I use the buffer inside of the putter (structs)?
For the most part, I understand what this errors means, not how it works. I just need a workaround or a solution. Any help is appreciated. I could have written this better but im tired.
This is more or less logical error for me. You should carefully read about Rust ownership.
Consider thinking about how would your code work.
struct Putter {
x: usize,
y: usize,
buffer: &'static mut Buffer,
}
Buffer is a reference which you have lifetime equal to the lifetime of the program,
'static tells compiler that. Error message is pretty clear, you're creating temporary value in place and borrowing it. It will be destroyed right after exiting scope, so your reference will be pointing to invalid memory. Dangling references are prohibited in Rust. In language like C++ this example will compile fine, but will result in run-time error.
To fix this issue buffer should own value and struct design should be rethought while keeping attention to lifetimes.
struct Putter {
x: usize,
y: usize,
buffer: Buffer,
}
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.