I am working on a project where I need an Ordered Set and I use a BTreeSet for that.
The Elements in my BTreeSet are PItem they are basically just a tuple of an Item and an OrderedFloat, like this: struct PItem((Item, OrderedFloat<f64>)) -> read like struct PItem(Item, weight)
Now the tricky part: My BTreeMap should order the PItems only based on the weight, disregarding the Item.
If my BTreeMap already contains an PItem on the other hand should only be determined by the Item, disregarding the weight. Here is my Implementation for Ord and Eq for Item and PItem:
use ordered_float::OrderedFloat;
use std::cmp::Ordering;
#[derive(Eq)]
pub struct Item{
pub s_i: u32,
pub e_i: u32,
pub val: String,
}
#[derive(Eq)]
pub struct PItem(pub Item, pub OrderedFloat<f64>);
impl Item{
pub fn start(&self) -> u32{
self.s_i
}
pub fn end(&self) -> u32{
self.e_i
}
pub fn val(&self) -> &str{
&self.val
}
}
impl PItem{
pub fn start(&self) -> u32{
self.item().start()
}
pub fn end(&self) -> u32{
self.item().end()
}
pub fn prob(&self) -> &OrderedFloat<f64>{
&(self.1)
}
pub fn val(&self) -> &str{
self.item().val()
}
pub fn item(&self) -> &Item{
return &self.0
}
}
impl PartialOrd for Item {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.s_i == other.s_i{
if self.e_i == other.e_i{
return self.val.partial_cmp(&(other.val));
}
return self.e_i.partial_cmp(&(other.e_i));
}
return self.s_i.partial_cmp(&(other.s_i));
}
}
impl PartialEq for Item {
fn eq(&self, other: &Self) -> bool {
self.s_i.eq(&(other.s_i)) && self.e_i.eq(&(other.e_i)) && self.val.eq(&(other.val))
}
}
impl Ord for PItem {
fn cmp(&self, other: &PItem) -> Ordering {
if self.prob() == other.prob(){
return self.item().cmp(&(other.item()));
}
return self.prob().cmp(&(other.prob()));
}
}
impl PartialOrd for PItem {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.prob() == other.prob(){
return self.item().partial_cmp(&(other.item()));
}
return self.prob().partial_cmp(&(other.prob()));
}
}
impl PartialEq for PItem {
fn eq(&self, other: &Self) -> bool {
self.item().eq(&(other.item()))
}
}
impl Ord for Item {
fn cmp(&self, other: &Item) -> Ordering {
if self.start() == other.start(){
if self.end() == other.end(){
return self.val().cmp(&(other.val()));
}
return self.end().cmp(&(other.end()));
}
return self.start().cmp(&(other.start()));
}
}
Now however, I see that I can add the "same" PItem twice to the BTreeMap, when they have different weights. When everything is equal including the weight, I cannot insert it.
What I want is that I already cannot insert it even if the weight is different but the Item's are equal. So apparently it takes the weight into consideration when deciding if an Item is already present in the set. Why is that? I cannot find my mistake in the code. When I check only for equality without adding any PItem to some set, the eq method seems to work fine.
Related
I would like to create BTreeMap in Rust but to compare my keys I need another data structure which I store in a variable. Unfortunately it seems, that the traits in Rust can't be defined locally and my Ord implementation can't depend on local variables.
Does it mean that I have to reimplement whole BTreeMap to take lambdas?
Though it's not optimal (because of the extra data/pointer you have to store and could mess up) you could store the local variable (or depending on size & usecase a reference to it) alongside your values:
use std::cmp::{PartialOrd, Ord, Ordering};
use std::collections::BTreeSet;
fn main() {
dbg!(by_distance_from(-5));
}
fn by_distance_from(x: i32) -> Vec<i32> {
let v = vec![-5, 0, 1, 3, 10];
struct Comp<'a> {
cmp: &'a Cmp,
v: i32,
}
struct Cmp {
x: i32,
}
let cmp = Cmp { x };
impl PartialOrd for Comp<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.v.abs_diff(self.cmp.x).partial_cmp(&other.v.abs_diff(other.cmp.x))
}
}
impl Ord for Comp<'_> {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
impl PartialEq for Comp<'_> {
fn eq(&self, other: &Self) -> bool {
self.v == other.v
}
}
impl Eq for Comp<'_> {}
let s: BTreeSet<_> = v.into_iter().map(|v| Comp {
cmp: &cmp,
v,
}).collect();
s.into_iter().map(|Comp { v, ..}| v).collect()
}
Playground
TL;DR. Is it possible to automatically implement a trait for an existing type that already has all methods required by the trait?
Long version. Suppose I want to have a generic function that does stack operations on any stack-like type. So I have a trait
pub trait StackLike<T> {
fn is_empty(&self) -> bool;
fn pop(&mut self) -> Option<T>;
fn push(&mut self, value: T);
}
which I can now use like this: pub fn foo(s: &mut dyn StackLike<i32>) { s.push(42); }. So far so good.
There are several existing types that already satisfy my trait, so implementing it is trivial, e.g.
impl<T> StackLike<T> for Vec<T> {
fn is_empty(&self) -> bool { self.is_empty() }
fn pop(&mut self) -> Option<T> { self.pop() }
fn push(&mut self, value: T) { self.push(value) }
}
impl<T, const N: usize> StackLike<T> for SmallVec<[T; N]> {
fn is_empty(&self) -> bool { self.is_empty() }
fn pop(&mut self) -> Option<T> { self.pop() }
fn push(&mut self, value: T) { self.push(value) }
}
This works, but it's a lot of boilerplate. Absolutely nothing interesting happens here: method names, argument types and return type — everything is same.
Question. Is it possible to avoid spelling out all these tautological statements, i.e. do something like the snippet below?
#[implement-trait-automagically]
impl<T> StackLike<T> for Vec<T>;
#[implement-trait-automagically]
impl<T, const N: usize> StackLike<T> for SmallVec<[T; N]>;
Or may be I can mark my trait somehow so that all types that can satisfy it automatically satisfy it (similar to how concepts work in C++)?
Rust doesn't have this kind of "duck typing" but you can put the implementation behind a macro to achieve your stated goals of avoiding all of the boilerplate code while keeping the implementations identical and error-free:
pub trait StackLike<T> {
fn is_empty(&self) -> bool;
fn pop(&mut self) -> Option<T>;
fn push(&mut self, value: T);
}
macro_rules! stacklike_impl {
() => {
fn is_empty(&self) -> bool { self.is_empty() }
fn pop(&mut self) -> Option<T> { self.pop() }
fn push(&mut self, value: T) { self.push(value) }
}
}
impl<T> StackLike<T> for Vec<T> { stacklike_impl!(); }
impl<T, const N: usize> StackLike<T> for SmallVec<[T; N]> { stacklike_impl!(); }
I'm trying to combine more than one condition when implementing cmp() and eq() from Ord and PartialEq traits. Something that looks like this:
self.id.cmp(&other.id) && self.age.cmp(&other.age)
Here's a working example minus the combined condition:
use std::cmp::Ordering;
#[derive(Debug, Clone, Eq)]
pub struct Person {
pub id: u32,
pub age: u32,
}
impl Person {
pub fn new(id: u32, age: u32) -> Self {
Self {
id,
age,
}
}
}
impl Ord for Person {
fn cmp(&self, other: &Self) -> Ordering {
self.id.cmp(&other.id)
}
}
impl PartialOrd for Person {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Person {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
Ord doesn't return a boolean, it returns an Ordering which can be Less, Equal or Greater, so you can't just use && on it.
Ordering has a few methods, one of which is then (and its companion then_with), which does the usual "sort by one field, and then by another one" operation. Your example then becomes
fn cmp(&self, other: &Self) -> Ordering {
self.id.cmp(&other.id)
.then(self.age.cmp(&other.age))
}
With Rust traits, I can express a Monoid type class (forgive me for the naming of the methods):
trait Monoid {
fn append(self, other: Self) -> Self;
fn neutral() -> Self;
}
Then, I can also implement the trait for strings or integers:
impl Monoid for i32 {
fn append(self, other: i32) -> i32 {
self + other
}
fn neutral() -> Self { 0 }
}
However, how could I now add another implementation on i32 for the multiplication case?
impl Monoid for i32 {
fn append(self, other: i32) -> i32 {
self * other
}
fn neutral() { 1 }
}
I tried something like what is done in functional but that solution seems to rely on having an additional type parameter on the trait instead of using Self for the elements, which gives me a warning.
The preferred solution would be using marker traits for the operations - something I also tried but didn't succeed in.
The answer, as pointed out by #rodrigo, is to use marker structs.
The following example shows a working snippet: playground
trait Op {}
struct Add;
struct Mul;
impl Op for Add {}
impl Op for Mul {}
trait Monoid<T: Op>: Copy {
fn append(self, other: Self) -> Self;
fn neutral() -> Self;
}
impl Monoid<Add> for i32 {
fn append(self, other: i32) -> i32 {
self + other
}
fn neutral() -> Self {
0
}
}
impl Monoid<Mul> for i32 {
fn append(self, other: i32) -> i32 {
self * other
}
fn neutral() -> Self {
1
}
}
pub enum List<T> {
Nil,
Cons(T, Box<List<T>>),
}
fn combine<O: Op, T: Monoid<O>>(l: &List<T>) -> T {
match l {
List::Nil => <T as Monoid<O>>::neutral(),
List::Cons(h, t) => h.append(combine(&*t)),
}
}
fn main() {
let list = List::Cons(
5,
Box::new(List::Cons(
2,
Box::new(List::Cons(
4,
Box::new(List::Cons(
5,
Box::new(List::Cons(-1, Box::new(List::Cons(8, Box::new(List::Nil))))),
)),
)),
)),
);
println!("{}", combine::<Add, _>(&list));
println!("{}", combine::<Mul, _>(&list))
}
I'd like to come up with a function that does a best-effort equality check on two values. For T: PartialEq it's easy, and for Rc<T> it's sufficient to use Rc::ptr_eq when T is not PartialEq.
I'm attempting to use specialization to provide the Rc<T: !PartialEq> case but I'm having problems with overlapping implementations, since Rc could implement PartialEq.
Is there any way to come up with a single ref_eq function, with or without specialization?
My code: (playground):
#![feature(specialization)]
use std::fmt::Display;
use std::rc::Rc;
trait RefEq {
fn ref_eq(&self, other: &Self) -> bool;
}
impl<T> RefEq for T {
default fn ref_eq(&self, _other: &Self) -> bool {
false
}
}
impl<T> RefEq for T where T: PartialEq {
default fn ref_eq(&self, other: &Self) -> bool {
self == other
}
}
// error[E0119]: conflicting implementations of trait `RefEq` for type `std::rc::Rc<_>`:
/*
impl<T> RefEq for Rc<T> {
fn ref_eq(&self, other: &Self) -> bool {
Rc::ptr_eq(self, other)
}
}
*/
fn assert_ref_eq<T>() where T: RefEq { }
fn main() {
assert_ref_eq::<f64>(); // f64 is PartialEq => ok
assert_ref_eq::<Rc<String>>(); // String is PartialEq => ok
assert_ref_eq::<Rc<dyn Display>>(); // dyn Display is not PartialEq =>
// please use Rc::ptr_eq
}