How to set the lifetime on these structures? - rust

I am trying to make an Iterator which filters out certain moves from a movelist. In order not to take ownership of the returned moves, they get referenced. However, when doing that, the missing lifetime specifier compiler error appears. As I have a chain of structs, I thought the first step to solve the problem was to start putting lifetimes on MoveFilter and then onto it's type Item in IntoIterator. However there it complains the usage of an undeclared lifetime.
Code:
pub struct GameMove {
pub from: usize,
pub to: usize,
pub move_type: GameMoveType,
pub piece_type: PieceType,
}
#[derive(PartialEq, Clone, Debug)]
pub enum GameMoveType {
Quiet,
Capture(PieceType),
}
#[derive(PartialEq, Clone, Debug)]
pub enum PieceType {
King,
Pawn
}
pub fn match_move_type(move_type: &GameMoveType) -> usize {
match move_type {
GameMoveType::Quiet => 0,
GameMoveType::Capture(_) => 1,
}
}
pub struct MoveFilter<'a> {
legal_moves: Vec<GameMove>,
move_type: GameMoveType,
}
impl IntoIterator for MoveFilter {
type Item = &'a GameMove;
type IntoIter = MoveFilterIterator;
fn into_iter(self) -> Self::IntoIter {
MoveFilterIterator {
legal_moves: self.legal_moves,
move_type: match_move_type(&self.move_type),
index: 0,
}
}
}
pub struct MoveFilterIterator {
legal_moves: Vec<GameMove>,
move_type: usize,
index: usize,
}
impl Iterator for MoveFilterIterator {
type Item = &GameMove;
fn next(&mut self) -> Option<&GameMove> {
while self.index < self.legal_moves.len() {
if match_move_type(&self.legal_moves[self.index].move_type) == self.move_type {
Some(&self.legal_moves[self.index])
} else {
self.index += 1;
}
}
None
}
}

There is a discrepancy between your
method fn into_iter(self: MoveFilter) that takes ownership of MoveFilter
and
the method fn next(&mut self) -> Option<&GameMove> that only wants to hand out immutable references to GameMoves. Who is supposed to own the referenced GameMoves after your into_iter takes ownership of the MoveFilter and completely consumes it?
One way to fix it would be to implement IntoIterator for &'a MoveFilter, that does not take ownership of MoveFilter, and thus does not have to worry that all GameMoves are discarded while there are any references &'a GameMove floating around:
pub struct GameMove {
pub from: usize,
pub to: usize,
pub move_type: GameMoveType,
pub piece_type: PieceType,
}
#[derive(PartialEq, Clone, Debug)]
pub enum GameMoveType {
Quiet,
Capture(PieceType),
}
#[derive(PartialEq, Clone, Debug)]
pub enum PieceType {
King,
Pawn
}
pub fn match_move_type(move_type: &GameMoveType) -> usize {
match move_type {
GameMoveType::Quiet => 0,
GameMoveType::Capture(_) => 1,
}
}
pub struct MoveFilter {
legal_moves: Vec<GameMove>,
move_type: GameMoveType,
}
impl<'t> IntoIterator for &'t MoveFilter {
type Item = &'t GameMove;
type IntoIter = MoveFilterIterator<'t>;
fn into_iter(self) -> Self::IntoIter {
MoveFilterIterator {
legal_moves: &self.legal_moves[..],
move_type: match_move_type(&self.move_type),
index: 0,
}
}
}
pub struct MoveFilterIterator<'a> {
legal_moves: &'a [GameMove],
move_type: usize,
index: usize,
}
impl<'a> Iterator for MoveFilterIterator<'a> {
type Item = &'a GameMove;
fn next(&mut self) -> Option<&'a GameMove> {
while self.index < self.legal_moves.len() {
if match_move_type(&self.legal_moves[self.index].move_type) == self.move_type {
return Some(&self.legal_moves[self.index])
} else {
self.index += 1;
}
}
None
}
}
Another possible solution would be to leave your IntoIterator for MoveFilter as-is, but then change Item = &GameMove to Item = GameMove. This would give you a destructive iterator that moves the MoveFilter and that can be used only once, but I assume that's not what you wanted when you began with type Item = &GameMove. Implementing it seems a bit more awkward, because it's not entirely trivial to remove single elements from a vector, and I didn't quite understand what that while-loop was doing there.

Related

How to define a Vec in Rust to support containing multiple types of structs that implement a same trait?

In c++, I can define a parent class, and the type in vector can just be the father class type. So how to implement that in Rust?
Like for this example:
I defined two types of Integer who both implement the trait Object, now I want to put them in a same vector, is there any way to achieve that?
pub trait Object<T: Object<T>+Clone> {
fn sub(&self, x: &T) -> T {
x.clone() //this is a useless implementation, just for structs don't need to implement this method.
}
}
#[derive(Debug, Copy, Clone)]
pub struct Integer {
val: i32
}
impl Integer {
pub fn get(&self) -> i32 {
self.val
}
pub fn new(val: i32) -> Self {
Integer {
val
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct Int {
val: i32
}
impl Int {
pub fn get(&self) -> i32 {
self.val
}
pub fn new(val: i32) -> Self {
Int {
val
}
}
}
impl Object<Int> for Int {
fn sub(&self, rhs: &Int) -> Int {
Int {
val: self.val - rhs.get()
}
}
}
impl Object<Integer> for Integer {
fn sub(&self, rhs: &Integer) -> Integer {
Integer {
val: self.val - rhs.get()
}
}
}
fn main() {
let mut v: Vec<Box<dyn Object>> = Vec::new();
v.push(Box::new(Integer::new(1)));
v.push(Box::new(Int::new(2)));
}
Thanks a lot.
There are several aspects of your design that don't fit in Rust:
trait Object<T: Object<T>+Clone> doesn't help - Rust doesn't do CRTP, just use Self instead.
for Object to be object-safe (necessary to put it in a vector), it can't be parameterized by type. A type parameter means you get a completely separate trait for each type.
Object::sub() can't return the result by value, because the size of the value can differ for different implementations, so it wouldn't be object-safe. It must return Box<dyn Object> instead.
The code modified as indicated looks like this:
pub trait Object {
fn get(&self) -> i32;
fn sub(&self, x: &dyn Object) -> Box<dyn Object>;
}
#[derive(Debug, Copy, Clone)]
pub struct Integer {
val: i32,
}
impl Integer {
fn new(val: i32) -> Box<dyn Object> {
Box::new(Int { val })
}
}
impl Object for Integer {
fn get(&self) -> i32 {
self.val
}
fn sub(&self, rhs: &dyn Object) -> Box<dyn Object> {
Integer::new(self.val - rhs.get())
}
}
#[derive(Debug, Copy, Clone)]
pub struct Int {
val: i32,
}
impl Int {
fn new(val: i32) -> Box<dyn Object> {
Box::new(Int { val })
}
}
impl Object for Int {
fn get(&self) -> i32 {
self.val
}
fn sub(&self, rhs: &dyn Object) -> Box<dyn Object> {
Int::new(self.val - rhs.get())
}
}
fn main() {
let mut v: Vec<Box<dyn Object>> = vec![];
v.push(Integer::new(1));
v.push(Int::new(2));
v.push(v[0].sub(v[1].as_ref()));
for o in &v {
println!("{}", o.get());
}
}
Playground
I think you can combine trait and provide a blank implementation, then use that in vector
trait TraitA {}
trait TraitB {}
trait CombinedTraitATraitB: TraitA + TraitB {}
impl<T> CombinedTraitATraitB for T where T: TraitA + TraitB {}
let vector: Vec<Box<dyn CombinedTraitATraitB>> = vec![];

Lifetime parameter in function call has conflicting requirements

I have a Screen struct which holds two mutable slices for buffers and one for a canvas:
pub struct Screen<'a> {
pub canvas: Canvas<'a>,
pub buffers: [Vec<u32>; 2],
pub index: usize,
}
Methods to borrow the buffers and a method do swap and write them:
impl<'a> Screen<'a> {
pub fn get_buf(&'a self) -> &[u32] {
self.buffers[self.index].as_slice()
}
pub fn get_mut_buf(&mut self) -> &mut [u32] {
self.buffers[self.index].as_mut_slice()
}
pub fn swap_buffers(&mut self) {
self.canvas.copy_from_slice(self.get_buf());
self.bufnum += 1;
if self.bufnum == self.buffers.len() {
self.bufnum = 0;
}
}
}
The issue here is that, as answered in another thread (Cannot infer correct lifetime when borrowing index of slice and field of a struct at once), the compiler can't see self.buffers and self.canvas as different things, so I tried to assign a new 'b lifetime to buffers, but it resulted in it having conflicting requirements somehow:
struct Screen<'a, 'b> {
pub canvas: &'a mut [u32],
pub buffers: [&'b mut [u32]; 2],
pub bufnum: usize,
}
impl<'a, 'b> Screen<'a, 'b> {
pub fn get_buf(&self) -> &'b [u32] {
self.buffers[self.bufnum]
}
pub fn get_mut_buf(&mut self) -> &'b mut [u32] {
&mut self.buffers[self.bufnum]
}
pub fn swap_buffers(&mut self) {
self.canvas.copy_from_slice(self.get_buf());
self.bufnum += 1;
if self.bufnum == self.buffers.len() {
self.bufnum = 0;
}
}
}
How can I solve this problem and still be able to use get_but and get_mut_buf?
Use your original struct.
match self {
Screen { index, buffers, index } => {
// the parts are separate &mut references here
....
}
}

How to properly solve lifetime inferences in struct?

The Rust playground code is here.
I have a struct of Token which has lifetime 'tok, and a scanner has lifetime 'lexer. I'm using both of them in another struct Parser, then I had a problem:
pub struct Token<'tok> {
pub value: Cow<'tok, str>,
pub line: usize,
}
pub struct Scanner {
pub source: Vec<char>,
pub current: usize,
pub line: usize,
}
pub struct Parser<'lexer> {
pub curr: &'lexer Token<'lexer>,
pub prev: &'lexer Token<'lexer>,
scanner: &'lexer mut Scanner,
}
impl <'lexer> Parser<'lexer> {
pub fn advance(&mut self) {
self.prev = self.curr;
self.curr = &self.scanner.next(); // cannot inference lifetime
}
}
I think the problem is Token has lifetime 'tok, and the borrow checker doesn't know the relation between 'tok and 'lexer so it can't inference proper lifetime.
However, I can avoid the problem by modifying it into the updated code:
pub struct Parser<'lexer> {
pub curr: Token<'lexer>,
pub prev: Token<'lexer>,
scanner: &'lexer mut Scanner,
}
impl <'lexer> Parser<'lexer> {
pub fn advance(&mut self) {
let prev = std::mem::replace(&mut self.curr, self.scanner.next());
self.prev = prev;
}
}
And with Token produced by next() is static:
impl Scanner {
pub fn next(&mut self) -> Token<'static> {
Token {
value: Cow::from(""),
line: 0,
}
}
}
It does compile but I think it's not ideal because all tokens are cloned from scanner into parser(they are not references anymore) and living until end of Parser's life. So memory usage is doubled. Is there a more proper way to deal with this?
Actualy your code structure is not ideal to deal with the borrow checker here why:
Token struct should be owned, the struct itself do not require any allocations (as the token is owned some indrection are required to allow prev <-> next switching)
None of the Parser or Lexer should own the base data so it will be easy to bound proper lifetime
In our case Vec<Char> is not a friendly type to work with (we do not need to own the data and this will be harder to make the comipler understand lifetimes), instead we're going to use an &'str but you could reproduce the exact behaviours with an &[char])
Here is an example that compile just fine
pub struct Token<'source> {
pub value: Cow<'source, str>,
pub line: usize,
}
pub struct Scanner<'source> {
pub source: &'source str,
pub current: usize,
pub line: usize,
}
pub struct Parser<'source> {
pub curr: Option<Token<'source>>,
pub prev: Option<Token<'source>>,
scanner: Scanner<'source>,
}
impl <'source>Scanner<'source> {
pub fn next(&'source /* DONT Forget to bound 'source to `self` */ self) -> Token<'source> {
Token {
value: Cow::from(self.source), /* `self.source` is bound to `'source` so the compiler understand that the token lifetime is the same than the source's one */
line: 0,
}
}
}
impl <'lexer> Parser<'lexer> {
pub fn advance(&'lexer mut self) {
self.prev = self.curr.take();
self.curr = Some(self.scanner.next());
}
}

How to properly trait object that works with object-unsafe types?

I want to create a vector containing trait objects that have a val method that returns a comparable value:
trait Comparable {
fn val(&self) -> Box<dyn std::cmp::Ord>;
}
struct Int64Array {}
impl Comparable for Int64Array {
fn val(&self, i: usize) -> Box<dyn std::cmp::Ord> {
Box::new(i+1)
}
}
struct StringArray {}
impl Comparable for StringArray {
fn val(&self, i: usize) -> Box<dyn std::cmp::Ord> {
Box::new(format!("foo_{}", i))
}
}
fn main() {
// each row is guaranteed to have vector elements with the same type
let rows: Vec<Box<dyn Comparable>> = vec![
Box::new(Int64Array{}),
Box::new(StringArray{}),
];
println!("{}", rows[0].val(0).cmp(rows[0].val(1)));
println!("{}", rows[1].val(0).cmp(rows[1].val(1)));
}
However, because std::cmp::Ord is not a object-safe trait, this won't compile. What's the recommend way to workaround this limitation?
Thanks everyone for the quick replies.
Combing suggestions from #FrancisGagné and #loganfsmyth, here is a workaround that avoids using Ord trait directly:
trait Comparable {
fn compare(&self, i: usize, j: usize) -> std::cmp::Ordering;
}
struct Int64Array {}
impl Comparable for Int64Array {
fn compare(&self, _i: usize, _j: usize) -> std::cmp::Ordering {
std::cmp::Ordering::Equal
}
}
struct StringArray {}
impl Comparable for StringArray {
fn compare(&self, _i: usize, _j: usize) -> std::cmp::Ordering {
std::cmp::Ordering::Equal
}
}
fn main() {
// each row is guaranteed to have vector elements with the same type
let rows: Vec<Box<dyn Comparable>> = vec![
Box::new(Int64Array{}),
Box::new(StringArray{}),
];
println!("{:#?}", rows[0].compare(0, 1));
println!("{:#?}", rows[1].compare(0, 1));
}
I wonder if there is other ways to implement this feature.

How to modify my constructor in order to accept either a slice or a reference to array or vector

This is a simplified example of my code:
#[derive(Debug, Clone, Copy)]
enum Data<'a> {
I32(&'a [i32]),
F64(&'a [f64]),
}
impl<'a> From<&'a [i32]> for Data<'a> {
fn from(v: &'a [i32]) -> Data<'a> {
Data::I32(v)
}
}
impl<'a> From<&'a [f64]> for Data<'a> {
fn from(v: &'a [f64]) -> Data<'a> {
Data::F64(v)
}
}
#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
name: &'a str,
data: Data<'a>,
}
impl<'a> DataVar<'a> {
fn new<T>(name: &'a str, data: T) -> Self
where
T: Into<Data<'a>>,
{
Self {
name,
data: data.into(),
}
}
}
First of all, considering that I need to cast different DataVars to the same vector, and I would like to avoid using trait objects, do you think my implementation is correct or do you have suggestions for improvement?
Now my main question. I can define new DataVars passing a slice, for instance as follows:
let x = [1, 2, 3];
let xvar = DataVar::new("x", &x[..]);
How can I modify my constructor so that it works not only with a slice, but also with a reference to array or vector? For instance I would like the following to work as well:
let x = [1, 2, 3];
let xvar = DataVar::new("x", &x);
EDIT:
Now I tried implementing the same code using a trait object instead of an enum, but the result is even worse... isn't there really any solution to this?
trait Data: std::fmt::Debug {}
impl Data for &[i32] {}
impl Data for &[f64] {}
#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
name: &'a str,
data: &'a dyn Data,
}
impl<'a> DataVar<'a> {
fn new<T>(name: &'a str, data: &'a T) -> Self
where
T: Data,
{
Self { name, data }
}
}
let x = [1, 2, 3];
let xvar = DataVar::new("x", &&x[..]);
To me, AsRef doesn't seem to be the right abstraction for two reasons: first, because it's possible (if unlikely) for a type to implement both AsRef<[i32]> and AsRef<[f64]>, and it's not clear what should happen in that case; and second, because there's already a built-in language feature (coercion) that can turn Vec<T> or &[T; n] into &[T], and you're not taking advantage of it.
What I'd like is to write a new function that looks basically like this:
fn new<T>(name: &'a str, data: &'a [T]) -> Self
where
// what goes here?
This will automatically work with &[T; n], &Vec<T>, &Cow<T>, etc. if we can tell the compiler what to do with T. It makes sense that you could make a trait that knows how to convert &'a [Self] to Data and is implemented for i32 and f64, so let's do that:
trait Item: Sized {
fn into_data<'a>(v: &'a [Self]) -> Data<'a>;
}
impl Item for i32 {
fn into_data<'a>(v: &'a [i32]) -> Data<'a> {
Data::I32(v)
}
}
impl Item for f64 {
fn into_data<'a>(v: &'a [f64]) -> Data<'a> {
Data::F64(v)
}
}
The trait bound on new becomes trivial:
impl<'a> DataVar<'a> {
fn new<T>(name: &'a str, data: &'a [T]) -> Self
where
T: Item,
{
Self {
name,
data: T::into_data(data),
}
}
}
I find this more readable than the version with From and AsRef, but if you still want From, you can easily add it with a generic impl:
impl<'a, T> From<&'a [T]> for Data<'a>
where
T: Item,
{
fn from(v: &'a [T]) -> Self {
T::into_data(v)
}
}
We can use the AsRef trait to convert references to arrays or vectors to slices. AsRef is a generic trait, so we need to introduce a second type parameter to represent the "intermediate type" (the slice type). After calling as_ref, we've got a slice that can be converted to a Data using into.
impl<'a> DataVar<'a> {
fn new<T, U>(name: &'a str, data: &'a T) -> Self
where
T: AsRef<U> + ?Sized,
U: ?Sized + 'a,
&'a U: Into<Data<'a>>,
{
Self {
name,
data: data.as_ref().into(),
}
}
}
Note however that the data parameter is now a reference: this is necessary because the lifetime of the reference returned by as_ref is bound by the lifetime of the self parameter passed to as_ref. If we changed the parameter back to data: T, then data.as_ref() now implicitly references data in order to call as_ref, which expects a shared reference to self (&self). But data here is a local parameter, which means that the lifetime of the reference created by this implicit referencing operation is limited to the local function, and so is the reference returned by data.as_ref(). This lifetime is shorter than 'a, so we can't store it in the DataVar and return it.
If you need to handle data values that are not references in addition to values that are references, this solution cannot support that, unfortunately.
This is actually the best solution for my case:
impl<'a> DataVar<'a> {
fn new<T, U>(name: &'a str, data: &'a T) -> Self
where
T: AsRef<[U]> + ?Sized,
U: 'a,
&'a [U]: Into<Data<'a>>,
{
Self {
name,
data: data.as_ref().into(),
}
}
}
It works with slices, references to vectors, and references to arrays up to length 32 which implement AsRef<[T]> https://doc.rust-lang.org/beta/std/convert/trait.AsRef.html
Thanks #Francis for your hints!
Actually, this is IMHO the best solution... so similar to my initial code, I just needed a small fix in the new constructor:
#[derive(Debug, Clone, Copy)]
enum Data<'a> {
I32(&'a [i32]),
F64(&'a [f64]),
}
impl<'a> From<&'a [i32]> for Data<'a> {
fn from(data: &'a [i32]) -> Data<'a> {
Data::I32(data)
}
}
impl<'a> From<&'a [f64]> for Data<'a> {
fn from(data: &'a [f64]) -> Data<'a> {
Data::F64(data)
}
}
#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
name: &'a str,
data: Data<'a>,
}
impl<'a> DataVar<'a> {
fn new<T>(name: &'a str, data: &'a [T]) -> Self
where
&'a [T]: Into<Data<'a>>,
{
Self {
name,
data: data.into(),
}
}
}
#trentcl your solution is brilliant! Now I see how to leverage coercion.
However I tweaked it a little bit as follows, I will finally use this code unless you see any drawbacks in it, thanks!
#[derive(Debug, Clone, Copy)]
enum Data<'a> {
I32(&'a [i32]),
F64(&'a [f64]),
}
trait IntoData<'a>: Sized {
fn into_data(&self) -> Data<'a>;
}
impl<'a> IntoData<'a> for &'a [i32] {
fn into_data(&self) -> Data<'a> {
Data::I32(&self)
}
}
impl<'a> IntoData<'a> for &'a [f64] {
fn into_data(&self) -> Data<'a> {
Data::F64(&self)
}
}
#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
name: &'a str,
data: Data<'a>,
}
impl<'a> DataVar<'a> {
fn new<T>(name: &'a str, data: &'a [T]) -> Self
where
&'a [T]: IntoData<'a>,
{
Self {
name,
data: data.into_data(),
}
}
}

Resources