I'd like to create a function that takes an x and y coordinate values and returns a string of the format (x,y):
pub struct Coord {
x: i32,
y: i32,
}
fn main() {
let my_coord = Coord {
x: 10,
y: 12
};
let my_string = coords(my_coord.x, my_coord.y);
fn coords(x: i32, y: i32) -> &str{
let l = vec!["(", x.to_string(), ",", y.to_string(), ")"];
let j = l.join("");
println!("{}", j);
return &j
}
}
This gives me the error:
|
14 | fn coords(x: i32, y: i32) -> &str {
| ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
help: consider using the `'static` lifetime
|
Adding the 'static lifetime seems to cause a host of other problems with this function? How can I fix this?
The more idiomatic approach would be to implement the Display trait for your type Coord which would allow you to call to_string() on it directly, and also would allow you to use it in the println! macro directly. Example:
use std::fmt::{Display, Formatter, Result};
pub struct Coord {
x: i32,
y: i32,
}
impl Display for Coord {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let my_coord = Coord { x: 10, y: 12 };
// create string by calling to_string()
let my_string = my_coord.to_string();
println!("{}", my_string); // prints "(10, 12)"
// can now also directly pass to println! macro
println!("{}", my_coord); // prints "(10, 12)"
}
playground
What you are trying to do is not possible. The String you are creating is local to the function and you are trying to return a reference to it.
j will be dropped at the end of the function, so you can't return a reference to it.
You will have to return a String:
fn coords(x: i32, y: i32) -> String {
let l = vec![
"(".into(),
x.to_string(),
",".into(),
y.to_string(),
")".into(),
];
let j = l.join("");
println!("{}", j);
return j;
}
Playground
A better way to do the same:
fn coords(x: i32, y: i32) -> String {
let x = format!("({},{})", x, y);
println!("{}", x);
return x;
}
Playground
this ended up working for me:
pub struct Coord{
x: i32,
y: i32,
}
fn main(){
let my_coord = Coord{
x: 10,
y: 12
};
let my_string = coords(my_coord.x, my_coord.y);
fn coords(x: i32, y: i32) -> String{
let myx = x.to_string();
let myy = y.to_string();
let l = vec!["(", &myx, ",", &myy, ")"];
let j = l.join("");
return j;
}
}
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
I have a mostly working implementation of a 2D grid in rust:
use itertools::Itertools;
#[derive(Debug)]
pub struct Grid<T> {
width: usize,
height: usize,
items: Vec<T>,
}
impl<T> Grid<T> {
pub fn new(width: usize, height: usize, initializer: impl Fn() -> T) -> Self {
Self {
width,
height,
items: (0..height * width).map(|_| initializer()).collect(),
}
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn size(&self) -> usize {
self.width * self.height
}
pub fn get(&self, x: usize, y: usize) -> Result<&T, &str> {
if self.on_grid(x as isize, y as isize) {
Ok(&self.items[self.coordinate_to_index(x, y)])
} else {
Err("coordinate not on grid")
}
}
pub fn get_mut(&mut self, x: usize, y: usize) -> Result<&mut T, &str> {
if self.on_grid(x as isize, y as isize) {
let index = self.coordinate_to_index(x, y);
Ok(&mut self.items[index])
} else {
Err("coordinate not on grid")
}
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.items.iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
self.items.iter_mut()
}
pub fn enumerate(&self) -> impl Iterator<Item = (usize, usize, &T)> {
self.items.iter().enumerate().map(|(index, item)| {
let (x, y) = self.index_to_coordinate(index);
(x, y, item)
})
}
pub fn enumerate_mut(&mut self) -> impl Iterator<Item = (usize, usize, &mut T)> {
self.items.iter_mut().enumerate().map(move |(index, item)| {
let (x, y) = self.index_to_coordinate(index);
(x, y, item)
})
}
pub fn neighbors(&self, x: usize, y: usize) -> impl Iterator<Item = (usize, usize, &T)> {
self.neighbor_indices(x, y)
.map(|(x, y)| (x, y, &self.items[self.coordinate_to_index(x, y)]))
}
fn coordinate_to_index(&self, x: usize, y: usize) -> usize {
y * self.width + x
}
fn index_to_coordinate(&self, index: usize) -> (usize, usize) {
let x = index % self.width;
let y = (index - x) / self.width;
(x, y)
}
fn neighbor_indices(&self, x: usize, y: usize) -> impl Iterator<Item = (usize, usize)> + '_ {
neighbor_offsets(2)
.map(move |item| (x as isize + item[0], y as isize + item[1]))
.filter(|&(x, y)| self.on_grid(x, y))
.map(|(dx, dy)| (dx as usize, dy as usize))
}
fn on_grid(&self, x: isize, y: isize) -> bool {
0 <= x && x < self.width as isize && 0 <= y && y < self.height as isize
}
}
fn neighbor_offsets(dimension: usize) -> impl Iterator<Item = Vec<isize>> {
(-1..1)
.map(|index| index as isize)
.combinations_with_replacement(dimension)
// skip zero offset
.filter(|items| !items.iter().all(|&item| item == 0))
}
Cargo.toml
[package]
name = "grid"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
itertools = "*"
However, with enumerate_mut() the compilation currently fails with
$ cargo build
Compiling either v1.8.0
Compiling itertools v0.10.5
Compiling grid v0.1.0 (/home/neumann/Projekte/grid)
error[E0505]: cannot move out of `self` because it is borrowed
--> src/lib.rs:64:47
|
63 | pub fn enumerate_mut(&mut self) -> impl Iterator<Item = (usize, usize, &mut T)> {
| - let's call the lifetime of this reference `'1`
64 | self.items.iter_mut().enumerate().map(move |(index, item)| {
| --------------------- ^^^^^^^^^^^^^^^^^^^^ move out of `self` occurs here
| |
| _________borrow of `self.items` occurs here
| |
65 | | let (x, y) = self.index_to_coordinate(index);
| | ---- move occurs due to use in closure
66 | | (x, y, item)
67 | | })
| |__________- returning this value requires that `self.items` is borrowed for `'1`
For more information about this error, try `rustc --explain E0505`.
error: could not compile `grid` due to previous error
warning: build failed, waiting for other jobs to finish...
I know that I could write the method index_to_coordinate() as a pure function, but I would like a solution, that uses the member function similarly to enumerate().
Can this be done? If so, how?
I would like a solution, that uses the member function similarly to enumerate(). Can this be done? If so, how?
It cannot be done. You cannot borrow self while self.items is mutably borrowed. Full stop. Consider this formulation that does work without a member function:
pub fn enumerate_mut(&mut self) -> impl Iterator<Item = (usize, usize, &mut T)> {
self.items.iter_mut().enumerate().map(|(index, item)| {
let x = index % self.width;
let y = (index - x) / self.width;
(x, y, item)
})
}
The only way this works is because of disjoint borrows, i.e. the compiler knows that .width and .height are completely separate objects from .items (and actually this only works in 2021 edition since the disjoint borrows happen both inside and outside the closure). If you use a member of self though, the compiler has lost that knowledge and has to consider that self can access items within that member function, which wouldn't be allowed since the mutable borrow earlier must stay exclusive. Using the member function in the enumerate() method works since immutable borrows can be shared.
Function signatures are the contracts that borrow-checking, type-checking, and type-inference build off of. If the member function has access to self, it must be assumed that it will access all of self. There is no syntax for partial-borrows across a function interface.
It cannot be done at all? Even if I used unsafe?
Even with unsafe, you are not allowed to violate Rust's borrowing rules, so you can't have a &Self while its .items is mutably borrowed. You can access the .width and .height but it must be done via a pointer: *const Self. Here's what that'd look like (tested with Miri on the playground):
pub fn enumerate_mut(&mut self) -> impl Iterator<Item = (usize, usize, &mut T)> {
let this = self as *const Self;
self.items.iter_mut().enumerate().map(move |(index, item)| {
let (x, y) = unsafe { Self::index_to_coordinate(this, index) };
(x, y, item)
})
}
unsafe fn index_to_coordinate(this: *const Self, index: usize) -> (usize, usize) {
let width = *std::ptr::addr_of!((*this).width);
let height = *std::ptr::addr_of!((*this).height);
let x = index % width;
let y = (index - x) / width;
(x, y)
}
However, I hope we can agree that this wouldn't be a member function anymore since it is lacking the self.index_to_coordinate syntax. Of course I'm not advocating you use this at all, it's just a demonstration that even going to the absurd it is not possible.
Just use a free function. :)
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)
}
}
I have some object that I want to split into two parts via a mutable borrow, then combine those back together into the original object when the split references go out of scope.
The simplified example below is for a Count struct that holds a single i32, which we want to split into two &mut i32s, who are both incorporated back into the original Count when the two mutable references go out of scope.
The approach I am taking below is to use an intermediate object CountSplit which holds a mutable reference to the original Count object and has the Drop trait implemented to do the re-combination logic.
This approach feels kludgy. In particular, this is awkward:
let mut ms = c.make_split();
let (x, y) = ms.split();
Doing this in one line like let (x, y) = c.make_split().split(); is not allowed because the intermediate object must have a longer lifetime. Ideally I would be able to do something like let (x, y) = c.magic_split(); and avoid exposing the intermediate object altogether.
Is there a way to do this which doesn't require doing two let's every time, or some other way to tackle this pattern that would be more idiomatic?
#[derive(Debug)]
struct Count {
val: i32,
}
trait MakeSplit<'a> {
type S: Split<'a>;
fn make_split(&'a mut self) -> Self::S;
}
impl<'a> MakeSplit<'a> for Count {
type S = CountSplit<'a>;
fn make_split(&mut self) -> CountSplit {
CountSplit {
top: self,
second: 0,
}
}
}
struct CountSplit<'a> {
top: &'a mut Count,
second: i32,
}
trait Split<'a> {
fn split(&'a mut self) -> (&'a mut i32, &'a mut i32);
}
impl<'a, 'b> Split<'a> for CountSplit<'b> {
fn split(&mut self) -> (&mut i32, &mut i32) {
(&mut self.top.val, &mut self.second)
}
}
impl<'a> Drop for CountSplit<'a> {
fn drop(&mut self) {
println!("custom drop occurs here");
self.top.val += self.second;
}
}
fn main() {
let mut c = Count { val: 2 };
println!("{:?}", c); // Count { val: 2 }
{
let mut ms = c.make_split();
let (x, y) = ms.split();
println!("split: {} {}", x, y); // split: 2 0
// each of these lines correctly gives a compile-time error
// c.make_split(); // can't borrow c as mutable
// println!("{:?}", c); // or immutable
// ms.split(); // also can't borrow ms
*x += 100;
*y += 5000;
println!("split: {} {}", x, y); // split: 102 5000
} // custom drop occurs here
println!("{:?}", c); // Count { val: 5102 }
}
playground:
I don't think a reference to a temporary value like yours can be made to work in today's Rust.
If it's any help, if you specifically want to call a function with two &mut i32 parameters like you mentioned in the comments, e.g.
fn foo(a: &mut i32, b: &mut i32) {
*a += 1;
*b += 2;
println!("split: {} {}", a, b);
}
you can already do that with the same number of lines as you'd have if your chaining worked.
With the chaining, you'd call
let (x, y) = c.make_split().split();
foo(x, y);
And if you just leave out the conversion to a tuple, it looks like this:
let mut ms = c.make_split();
foo(&mut ms.top.val, &mut ms.second);
You can make it a little prettier by e.g. storing the mutable reference to val directly in CountSplit as first, so that it becomes foo(&mut ms.first, &mut ms.second);. If you want it to feel even more like a tuple, I think you can use DerefMut to be able to write foo(&mut ms.0, &mut ms.1);.
Alternatively, you can of course formulate this as a function taking a function
impl Count {
fn as_split<F: FnMut(&mut i32, &mut i32)>(&mut self, mut f: F) {
let mut second = 0;
f(&mut self.val, &mut second);
self.val += second;
}
}
and then just call
c.as_split(foo);
I need to push an item to a vector. But both of item and vector are function parameters.
Simple code to reproduce:-
struct Point {
x: i32,
y: i32
}
fn main(){
let points: Vec<&Point> = vec!();
let start_point = Point {x: 1, y:2};
fn fill_points<F>(points: &mut Vec<&F>, point: &F ){
// Some conditions and loops
points.push(point);
}
fill_points(&mut points, & start_point);
}
Error:-
|
13 | fn fill_points<F>(points: &mut Vec<&F>, point: &F ){
| -- -- these two types are declared with different lifetimes...
14 | // Some conditions
15 | points.push(point);
| ^^^^^ ...but data from `point` flows into `points` here
Playground URL:- https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e1f2d738a22795ae11aab01ad8d00d96
You need to add a lifetime parameter:
struct Point {
x: i32,
y: i32,
}
fn main() {
let start_point = Point { x: 1, y: 2 };
let mut points: Vec<&Point> = vec![];
fn fill_points<'a, F>(points: &mut Vec<&'a F>, point: &'a F) {
// Some conditions
points.push(point);
}
fill_points(&mut points, &start_point);
}
(code simplified based the comment from #Stargateur)