Is it possible to conditionally implement a trait in Rust, in a way that is equivalent to the following pseudocode?
trait MyTrait {
fn my_trait(&self) -> u32;
}
impl<T> MyTrait for T where T: SomeTrait1 {
fn my_trait(&self) -> u32 {
1
}
} else where T: SomeTrait2 {
fn my_trait(&self) -> u32 {
2
}
} else {
fn my_trait(&self) -> u32 {
0
}
}
How can this be implemented using stable Rust and/or in nightly with the upcoming specialization RFCs?
The following works:
#![feature(specialization)]
trait MyTrait {
fn my_trait(&self) -> u32;
}
trait SomeTrait1 {}
trait SomeTrait2 {}
trait HasTrait1 {
const HAS_TRAIT_1: bool;
}
impl<T> HasTrait1 for T {
default const HAS_TRAIT_1: bool = false;
}
impl<T: SomeTrait1> HasTrait1 for T {
const HAS_TRAIT_1: bool = true;
}
trait HasTrait2 {
const HAS_TRAIT_2: bool;
}
impl<T> HasTrait2 for T {
default const HAS_TRAIT_2: bool = false;
}
impl<T: SomeTrait2> HasTrait2 for T {
const HAS_TRAIT_2: bool = true;
}
impl<T> MyTrait for T {
fn my_trait(&self) -> u32 {
if T::HAS_TRAIT_1 {
1
} else if T::HAS_TRAIT_2 {
2
} else {
0
}
}
}
impl SomeTrait1 for u16 {}
impl SomeTrait2 for u32 {}
fn main() {
dbg!(1u8.my_trait());
dbg!(1u16.my_trait());
dbg!(1u32.my_trait());
}
But it requires the specialization feature which is incomplete. There is the min_specialization feature, but I don't know how to use it and I don't know whether it works with this code.
Related
I am implementing my own blocking pub/sub pattern as seen below.
use std::any::{Any, TypeId};
#[derive(Clone)]
struct ModelA {
pub id: u32
}
#[derive(Clone)]
struct ModelB {
pub id: u32
}
struct ModelASubscriberOne<'a> {
pub model: &'a ModelA
}
impl<'a> Subscriber<u32> for ModelASubscriberOne<'a> {
fn get_type(&self) -> TypeId {
TypeId::of::<ModelASubscriberOne>()
}
fn execute(&self) {
println!("SubscriberOne ModelA id: {}", self.model.id);
}
fn id(&self) -> u32 {
self.model.id
}
}
struct ModelASubscriberTwo<'a> {
pub model: &'a ModelA
}
impl<'a> Subscriber<u32> for ModelASubscriberTwo<'a> {
fn get_type(&self) -> TypeId {
TypeId::of::<ModelASubscriberTwo>()
}
fn execute(&self) {
println!("SubscriberTwo ModelA id: {}", self.model.id);
}
fn id(&self) -> u32 {
self.model.id
}
}
trait Subscriber<TKey:Eq + 'static> {
fn get_type(&self) -> TypeId;
fn execute(&self);
fn id(&self) -> TKey;
fn filter(&self, other: TKey) -> bool{
self.id() == other
}
}
struct Publisher<'a> {
pub subscribers: Vec<&'a dyn Subscriber<u32>>
}
impl<'a> Publisher<'a> {
fn new() -> Publisher<'a> {
Publisher{subscribers: Vec::new()}
}
fn subscribe(&mut self, subscriber: &'a dyn Subscriber<u32>) {
self.subscribers.push(subscriber);
}
fn publish<T>(&self) where T: 'static + Subscriber<u32> {
self.subscribers.iter().filter(|x| {
TypeId::of::<T>() == x.get_type()
})
.for_each(|x| {
x.execute();
});
}
fn unsubscribe<T:Any>(&mut self, subscriber: u32) {
let position = self.subscribers.iter().position(|x| {
TypeId::of::<T>() == x.get_type() && x.filter(subscriber)
});
match position {
Some(position) => {
self.subscribers.remove(position);
},
_ => {}
}
}
}
fn main() {
let model_a = ModelA{id:0};
let mut model_a_list: Vec<ModelA> = Vec::new();
model_a_list.push(model_a);
let model_a = ModelA{id:1};
model_a_list.push(model_a);
let mut publisher = Publisher::new();
let subscriber = ModelASubscriberOne{model: &model_a_list[0]};
publisher.subscribe(&subscriber);
let subscriber = ModelASubscriberOne{model: &model_a_list[1]};
publisher.subscribe(&subscriber);
let subscriber = ModelASubscriberTwo{model: &model_a_list[1]};
publisher.subscribe(&subscriber);
println!("Subscribed");
publisher.publish::<ModelASubscriberOne>();
publisher.publish::<ModelASubscriberTwo>();
publisher.unsubscribe::<ModelASubscriberOne>(model_a_list[1].id);
println!("Unsubscribed");
publisher.publish::<ModelASubscriberOne>();
publisher.publish::<ModelASubscriberTwo>();
}
I feel like it would be helpful to be able to have a default implementation of get_type() for Subscriber that returns a TypeId for Self. Is there a way to do this?
I tried the following:
trait Subscriber<TKey:Eq + 'static> {
fn get_type(self: &Self) -> TypeId {
TypeId::of::<Self>()
}
// <...>
}
I get the following message:
error[E0310]: the parameter type `Self` may not live long enough
--> src\main.rs:51:5
|
51 | TypeId::of::<Self>()
| ^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `Self: 'static`...
= note: ...so that the type `Self` will meet its required lifetime bounds
I am not sure where to put the lifetime bound. I expect I need an Any trait bound as well but I am not sure where to put that either.
Thanks in advance.
Playground link
I have several different structs grouped together in an enum:
pub enum Ty {
A(AStruct),
B(BStruct)
}
pub struct AStruct {
base: BaseStruct
}
impl AStruct {
base_struct_passthrough_impls!();
pub fn new(x: i32) -> Self {
Self {
base: BaseStruct::new(x)
}
}
}
pub struct BStruct {
base: BaseStruct
}
impl BStruct {
base_struct_passthrough_impls!();
pub fn new(x: i32) -> Self {
Self {
base: BaseStruct::new(x)
}
}
}
All of these types will share a base struct that is common to them all. This base struct will have a lot of methods that I don't want to duplicate for each supertype.
pub struct BaseStruct {
x: i32
}
impl BaseStruct {
pub fn new(x: i32) -> Self {
Self {
x
}
}
pub fn get_x(&self) -> i32 {
self.x
}
}
#[macro_export]
macro_rules! base_struct_passthrough_impls {
() => {
pub fn get_x(&self) -> i32 {
self.base.get_x()
};
}
}
However, trying to use this code results in the following error:
error: macro expansion ignores token `;` and any following
--> src/main.rs:37:10
|
37 | };
| ^
...
46 | base_struct_passthrough_impls!();
| --------------------------------- caused by the macro expansion here
|
= note: the usage of `base_struct_passthrough_impls!` is likely invalid in impl item context
It seems like macro_rules!() is not usable in the impl item context. Is this correct, and if so is there anyway around this restriction? Would a proc macro work here, or would doing something like this work better?
The issue isn't the macro usage, but the definition. You included a trailing semicolon after the function definition created by the macro, which is what causes the error. If you remove it, everything works fine - here's the code:
fn main() {
let types = vec![Ty::A(AStruct::new(32)), Ty::B(BStruct::new(64))];
types.iter().for_each(|item| {
dbg!(match item {
Ty::A(a_struct) => a_struct.get_x(),
Ty::B(b_struct) => b_struct.get_x(),
});
})
}
pub enum Ty {
A(AStruct),
B(BStruct)
}
pub struct BaseStruct {
x: i32
}
impl BaseStruct {
pub fn new(x: i32) -> Self {
Self {
x
}
}
pub fn get_x(&self) -> i32 {
self.x
}
}
#[macro_export]
macro_rules! base_struct_passthrough_impls {
() => {
pub fn get_x(&self) -> i32 {
self.base.get_x()
} // there was an illegal semicolon here
}
}
pub struct AStruct {
base: BaseStruct
}
impl AStruct {
base_struct_passthrough_impls!();
pub fn new(x: i32) -> Self {
Self {
base: BaseStruct::new(x)
}
}
}
pub struct BStruct {
base: BaseStruct
}
impl BStruct {
base_struct_passthrough_impls!();
pub fn new(x: i32) -> Self {
Self {
base: BaseStruct::new(x)
}
}
}
I have a Template struct implementing a encoder function that returns a reference to a Boxed Encoder.
I also have a FixedEncoder struct that implements Encoder
I can create the Template and get the Encoder out, but how do I test the functions of FixedEncoder? I'm only looking to get FixedEncoder for testing purposes, so "unsafe" solutions are fine (though safe ones are preferred)
In my following example I get the error
error[E0599]: no method named `length` found for type `&std::boxed::Box<(dyn Encoder + 'static)>` in the current scope
Example (playground):
pub struct Template {
encoder: Box<Encoder>
}
impl Template {
fn new(encoder: Box<Encoder>) -> Template {
Template { encoder }
}
fn encoder(&self) -> &Box<Encoder> {
&self.encoder
}
}
pub trait Encoder {
fn isEncoder(&self) -> bool {
true
}
}
pub struct FixedEncoder {
length: usize
}
impl FixedEncoder {
pub fn new(length: usize) -> FixedEncoder {
FixedEncoder { length }
}
pub fn length(&self) -> usize {
self.length
}
}
impl Encoder for FixedEncoder {}
fn main() {
let fixed_encoder = FixedEncoder::new(1);
let template = Template::new(Box::new(fixed_encoder));
assert_eq!(template.encoder().isEncoder(), true); // works
assert_eq!(&template.encoder().length(), 1); // error[E0599]: no method named `length` found for type `&std::boxed::Box<(dyn Encoder + 'static)>` in the current scope
}
I was able to accomplish this by using Any.
Add an as_any declaration to Encoder
Add an as_any function to FixedEncoder
Use .as_any().downcast_ref().unwrap() on the retreived Encoder
playground
use std::any::Any;
pub struct Template {
encoder: Box<Encoder>
}
impl Template {
fn new(encoder: Box<Encoder>) -> Template{
Template {
encoder
}
}
fn encoder(&self) -> &Box<Encoder> {
&self.encoder
}
}
pub trait Encoder {
fn isEncoder(&self) -> bool {
true
}
fn as_any(&self) -> &dyn Any;
}
pub struct FixedEncoder {
length: usize
}
impl FixedEncoder {
pub fn new(length: usize) -> FixedEncoder {
FixedEncoder { length }
}
pub fn length(&self) -> usize {
self.length
}
}
impl Encoder for FixedEncoder {
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let fixed_encoder = FixedEncoder::new(1);
let template = Template::new(Box::new(fixed_encoder));
assert_eq!(template.encoder().isEncoder(), true); // works
let fixed_encoder_from_template : &FixedEncoder = &template.encoder().as_any().downcast_ref().unwrap();
assert_eq!(&fixed_encoder_from_template.length, &(1 as usize));
}
I have a trait RawFd that is implemented for each type that satisfies special requirements.
Now I have a function that accepts any type that implements RawFd. And I have a trait with an implementation for each T that implements RawFd.
However I cannot call my function from my trait even if T implements RawFd:
pub trait RawFd {
fn raw_fd(&self) -> u64;
}
#[cfg(unix)]
impl<T: std::os::unix::io::AsRawFd> RawFd for T {
fn raw_fd(&self) -> u64 { self.as_raw_fd() as u64 }
}
#[cfg(windows)]
impl<T: std::os::windows::io::AsRawSocket> RawFd for T {
fn raw_fd(&self) -> u64 { self.as_raw_socket() as u64 }
}
fn print_fd<T: RawFd>(p: T) {
println!("{}", p.raw_fd());
}
trait Printable {
fn print(&self);
}
impl<T: RawFd> Printable for T {
fn print(&self) {
print_fd(self);
}
}
What is my mistake here and how can I solve it.
(Link to playground)
If you expect to use print_fd() with a reference you must do this:
fn print_fd(p: &impl RawFd) {
println!("{}", p.raw_fd());
}
or
fn print_fd<T: RawFd>(p: &T) {
println!("{}", p.raw_fd());
}
Or you must take self by value:
impl<T: RawFd> Printable for T {
fn print(self) {
print_fd(self);
}
}
Or you could use clone:
impl<T> Printable for T where T: Clone + RawFd {
fn print(&self) {
print_fd(self.clone());
}
}
I have a Rust program which contains a number of different structs which all implement a trait called ApplyAction. Another struct, ActionList, contains a vector of boxed objects which implement ApplyAction. I would like to create some unit tests which compare ActionLists with one another.
There are a few different SO questions which deal with PartialEq on boxed traits, and I've used these to get some way towards an implementation. However, in the (simplified) code below (and on the Playground), the assertions in main() fail because the type ids of the objects passed to eq() differ. Why?
Also, this seems extremely complicated for such a simple use case -- is there an easier way to do this?
use std::any::TypeId;
use std::boxed::Box;
use std::fmt;
use std::mem::transmute;
#[derive(Debug, Eq, PartialEq)]
pub struct MyAction<T: fmt::Debug> {
label: T,
}
impl<T: fmt::Debug> MyAction<T> {
pub fn new(label: T) -> MyAction<T> {
MyAction { label: label }
}
}
pub trait ApplyAction<T: fmt::Debug + PartialEq>: fmt::Debug {
fn get_type(&self) -> TypeId;
fn is_eq(&self, other: &ApplyAction<T>) -> bool;
}
impl<T: fmt::Debug + Eq + 'static> ApplyAction<T> for MyAction<T> {
fn get_type(&self) -> TypeId {
TypeId::of::<MyAction<T>>()
}
fn is_eq(&self, other: &ApplyAction<T>) -> bool {
if other.get_type() == TypeId::of::<Self>() {
// Rust thinks that self and other are different types in the calls below.
let other_ = unsafe { *transmute::<&&ApplyAction<T>, &&Self>(&other) };
self.label == other_.label
} else {
false
}
}
}
impl<T: fmt::Debug + Eq + PartialEq + 'static> PartialEq for ApplyAction<T> {
fn eq(&self, other: &ApplyAction<T>) -> bool {
if other.get_type() == TypeId::of::<Self>() {
self.is_eq(other)
} else {
false
}
}
}
#[derive(Debug)]
pub struct ActionList<T: fmt::Debug> {
actions: Vec<Box<ApplyAction<T>>>,
}
impl<T: fmt::Debug + PartialEq> ActionList<T> {
pub fn new() -> ActionList<T> {
ActionList { actions: vec![] }
}
pub fn push<A: ApplyAction<T> + 'static>(&mut self, action: A) {
self.actions.push(Box::new(action));
}
}
impl<T: fmt::Debug + Eq + PartialEq + 'static> PartialEq for ActionList<T> {
fn eq(&self, other: &ActionList<T>) -> bool {
for (i, action) in self.actions.iter().enumerate() {
if **action != *other.actions[i] {
return false;
}
}
true
}
}
fn main() {
let mut script1: ActionList<String> = ActionList::new();
script1.push(MyAction::new("foo".to_string()));
let mut script2: ActionList<String> = ActionList::new();
script2.push(MyAction::new("foo".to_string()));
let mut script3: ActionList<String> = ActionList::new();
script3.push(MyAction::new("bar".to_string()));
assert_eq!(script1, script2);
assert_ne!(script1, script3);
}
In the impl<...> PartialEq for ApplyAction<T> you used TypeId::of::<Self>(); i.e. the type of the unsized trait object. That isn't what you wanted; but remove the if and directly call self.is_eq(other), and your code should be working.
Sadly your example requires a lot of code to implement ApplyAction<T> for MyAction<T> - and again for each other action type you might want to use.
I tried to remove that overhead, and with nightly features it is completely gone (and otherwise only a small stub remains):
Playground
// see `default impl` below
#![feature(specialization)]
// Any::<T>::downcast_ref only works for special trait objects (`Any` and
// `Any + Send`); having a trait `T` derive from `Any` doesn't allow you to
// coerce ("cast") `&T` into `&Any` (that might change in the future).
//
// Implementing a custom `downcast_ref` which takes any
// `T: Any + ?Sized + 'static` as input leads to another problem: if `T` is a
// trait that didn't inherit `Any` you still can call `downcast_ref`, but it
// won't work (it will use the `TypeId` of the trait object instead of the
// underlying (sized) type).
//
// Use `SizedAny` instead: it's only implemented for sized types by default;
// that prevents the problem above, and we can implement `downcast_ref` without
// worrying.
mod sized_any {
use std::any::TypeId;
// don't allow other implementations of `SizedAny`; `SizedAny` must only be
// implemented for sized types.
mod seal {
// it must be a `pub trait`, but not be reachable - hide it in
// private mod.
pub trait Seal {}
}
pub trait SizedAny: seal::Seal + 'static {
fn get_type_id(&self) -> TypeId {
TypeId::of::<Self>()
}
}
impl<T: 'static> seal::Seal for T {}
impl<T: 'static> SizedAny for T {}
// `SizedAny + ?Sized` means it can be a trait object, but `SizedAny` was
// implemented for the underlying sized type.
pub fn downcast_ref<From, To>(v: &From) -> Option<&To>
where
From: SizedAny + ?Sized + 'static,
To: 'static,
{
if TypeId::of::<To>() == <From as SizedAny>::get_type_id(v) {
Some(unsafe { &*(v as *const From as *const To) })
} else {
None
}
}
}
use sized_any::*;
use std::boxed::Box;
use std::fmt;
// `ApplyAction`
fn foreign_eq<T, U>(a: &T, b: &U) -> bool
where
T: PartialEq + 'static,
U: SizedAny + ?Sized + 'static,
{
if let Some(b) = downcast_ref::<U, T>(b) {
a == b
} else {
false
}
}
pub trait ApplyAction<T: 'static>: fmt::Debug + SizedAny + 'static {
fn foreign_eq(&self, other: &ApplyAction<T>) -> bool;
}
// requires `#![feature(specialization)]` and a nightly compiler.
// could also copy the default implementation manually to each `impl` instead.
//
// this implementation only works with sized `A` types; we cannot make
// `ApplyAction<T>` inherit `Sized`, as that would destroy object safety.
default impl<T: 'static, A: PartialEq + 'static> ApplyAction<T> for A {
fn foreign_eq(&self, other: &ApplyAction<T>) -> bool {
foreign_eq(self, other)
}
}
impl<T: 'static> PartialEq for ApplyAction<T> {
fn eq(&self, other: &ApplyAction<T>) -> bool {
self.foreign_eq(other)
}
}
// `MyAction`
#[derive(Debug, Eq, PartialEq)]
pub struct MyAction<T: fmt::Debug> {
label: T,
}
impl<T: fmt::Debug> MyAction<T> {
pub fn new(label: T) -> MyAction<T> {
MyAction { label: label }
}
}
impl<T: fmt::Debug + PartialEq + 'static> ApplyAction<T> for MyAction<T> {}
// `ActionList`
#[derive(Debug)]
pub struct ActionList<T> {
actions: Vec<Box<ApplyAction<T>>>,
}
impl<T: 'static> ActionList<T> {
pub fn new() -> ActionList<T> {
ActionList { actions: vec![] }
}
pub fn push<A: ApplyAction<T> + 'static>(&mut self, action: A) {
self.actions.push(Box::<A>::new(action));
}
}
impl<T: 'static> PartialEq for ActionList<T> {
fn eq(&self, other: &ActionList<T>) -> bool {
if self.actions.len() != other.actions.len() {
return false;
}
for (i, action) in self.actions.iter().enumerate() {
if **action != *other.actions[i] {
return false;
}
}
true
}
}
// `main`
fn main() {
let mut script1: ActionList<String> = ActionList::new();
script1.push(MyAction::new("foo".to_string()));
let mut script2: ActionList<String> = ActionList::new();
script2.push(MyAction::new("foo".to_string()));
let mut script3: ActionList<String> = ActionList::new();
script3.push(MyAction::new("bar".to_string()));
assert_eq!(script1, script2);
assert_ne!(script1, script3);
}
See also:
Object Safety