I'm just learning Rust, so maybe I just didn't get some concepts correctly.
I have a trait with some implementations:
trait Abstract {
fn name(&self) -> &str;
}
struct Foo {}
struct Bar {}
struct Baz {}
impl Abstract for Foo {
fn name(&self) -> &str { "foo" }
}
impl Abstract for Bar {
fn name(&self) -> &str { "bar" }
}
impl Abstract for Baz {
fn name(&self) -> &str { "baz" }
}
I want to add a static method to this trait to create some standard implementation by name:
trait Abstract {
fn new(name: &str) -> Self {
match name {
"foo" => Foo{},
"bar" => Bar{},
"baz" => Baz{},
}
}
fn name(&self) -> &str;
}
But this code doesn't compile because:
6 | fn new(name: &str) -> Self {
| ^^^^ doesn't have a size known at compile-time
I tried to use return fn new(name: &str) -> impl Abstract and
fn new<T: Abstract>(name: &str) -> T - but these variants doesn't work too with another errors.
What is the correct way exist of creating factory method for trait in Rust?
In Rust, every variable must be a single specific type. This is different to OO languages where a variable can have a type Foo, and that means that the variable contains a Foo, or any subclass of Foo.
Rust doesn't support this. If a variable has a type Foo, it must contain a Foo (ignoring any unsafe concerns).
Rust also doesn't support using traits as types (without the dyn keyword).
In your example, you have:
trait Abstract {
fn new(name: &str) -> Self {
// ...
}
}
The return type Self here means "whatever type this trait is being implemented on". However, by providing a body in the trait definition, you're providing a default implementation, so this function should in theory apply to any type, and so the compiler has no information about the real concrete type Self, and therefore the Sized bound it not met (which in practice is very restrictive and probably not what you want).
If you want a function that takes a string and returns "some type T that implements Abstract", you could use a "trait object", which would look roughly like:
// outside trait definition
fn new_abstract(name: &str) -> Box<dyn Abstract> { // dyn keyword opts into dynamic dispatch with vtables
match name {
"foo" => Box::new(Foo {}),
// ...
}
}
However, I'd warn against this pattern. Dynamic dispatch has some runtime overhead, and prevents many compile-time optimizations. Instead, there may be a more "rusty" way of doing it, but it's hard to tell without more context.
In general, constructing a type based on the value of a string smells like a bit of an anti-pattern.
You can consider using an enum instead of dynamically dispatch it:
trait Abstract {
fn name(&self) -> &str;
fn new(name: &str) -> Option<AbstractVariant> {
match name {
"foo" => Some(AbstractVariant::Foo(Foo {})),
"bar" => Some(AbstractVariant::Bar(Bar {})),
"baz" => Some(AbstractVariant::Baz(Baz {})),
_ => None,
}
}
}
enum AbstractVariant {
Foo(Foo),
Bar(Bar),
Baz(Baz),
}
Playground
fn new<T: Abstract>(…) -> T means: new will return some concrete T that implements Abstract, and whatever calls new may decide which.
fn new() -> impl Abstract means: new will return some concrete type of its choosing that implements Abstract, but whatever calls new won't know which concrete type. (But it must still be some single concrete type decided at compile-time, it can't be "Maybe Foo or maybe Baz")
If you absolutely want this, you can have a free function do your constructing:
trait Abstract {
fn name(&self) -> &str;
}
fn new_abstract(name: &str) -> Box<dyn Abstract> {
match name {
"foo" => Box::new(Foo{}),
"bar" => Box::new(Bar{}),
"baz" => Box::new(Baz{}),
_ => panic!("better not")
}
}
The new function can't be part of Abstract, because you would get into trouble with object safety. Essentially, if you want to return a Box<dyn …> of some trait, all the methods on the trait must be usable in dynamic dispatch, but new is not such a method.
Anyway, this is not a good idea. Adding a separate new function to Foo, Bar, and Baz is the Rust way to go.
Related
I've thinned this down and changed to a channel, so that it might make more sense. I'd like to store an impl which is a predicate function, in my Struct, the code follows...
Start with a simple function that returns an impl for testing something.
fn make_test<T>(sender: Sender<T>) -> impl Fn()->bool {
let closure = move|| {sender.is_full()};
closure
}
struct MyStruct <T> {
sender: Sender<T>,
parker: Arc<Mutex<MyParker>>,
}
impl<T> MyStruct<T> {
fn something(&self) {
Here we are creating it and passing it as a parameter.
let test = make_test(self.sender.clone());
&self.parker.lock().unwrap().parked(test);
}
}
struct MyParker {
test: impl Fn()->bool,
}
impl MyParker {
fn parked(&mut self, test: impl Fn()->bool) {
The parameter is accepted as test, no issue there. But how do I declare it in the Struct so that I can save it for later?
self.test = test;
}
}
As I've currently declared it, it throws a compiler error:
error[E0562]: impl Trait not allowed outside of function and inherent method return types
I'm hoping that's a result of me doing something wrong, as opposed to you can't store an impl in a Struct. I've tried: Arc<>, Box<>, all to no avail.
"an impl" is not a type. It's, more or less, syntactic sugar for an unnamed type (in return value position) or type parameter (in argument position).
fn make_test<T>(sender: Sender<T>) -> impl Fn()->bool {
In the return position, it means "there is a specific concrete type, but I'm not writing it out, and it might change, but it will definitely implement Fn() -> bool". It is needed to return bare closures, which have otherwise unnameable types. (If the type of a closure could be written, it would have to specify the types of all the closed-over values.)
fn parked(&mut self, test: impl Fn()->bool) {
This is pure sugar for
fn parked<T: impl Fn()->bool>(&mut self, test: T) {
That is, parked is a generic function.
Therefore, you cannot just have a struct field of type impl ..., because it isn't a concrete type. What you can do are the same things as any other time you have more than one type: you can write a struct with a type parameter,
struct MyParker<F: Fn() -> bool + 'static> {
test: F,
}
but that won't work because you can't constrain things to the unknown closure type — or you can write a struct that contains a dyn value.
struct MyParker {
test: Box<dyn Fn() -> bool + 'static>,
}
impl MyParker {
fn parked(&mut self, test: impl Fn() -> bool + 'static) {
self.test = Box::new(test);
}
}
Note that in order to store the Fn we need to provide a lifetime bound. I've written it as 'static here, but you can also use a lifetime parameter.
But if you want to avoid the cost of dynamic dispatch, you're better off replacing your closure with an explicitly defined struct and method — or, if applicable (I haven't looked at what you're actually trying to do), just making MyParker be that struct so that it has a field of type Sender<T>.
I have two traits, one ordinal (Foo), another generic (TypedFoo<T>). I have several structures, each of them have both traits implemented.
Is it possible to convert from Foo to TypedFoo<T>, without converting to an intermediate structure?
trait Foo {
fn f(&mut self);
}
trait TypedFoo<T> {
fn get(&self) -> T;
}
#[derive(Clone)]
struct Data(i32);
impl Foo for Data {
fn f(&mut self) {
self.0 += 1;
}
}
impl TypedFoo<Data> for Data {
fn get(&self) -> Data {
self.clone()
}
}
//struct Data2(f64);
//impl Foo for Data2...
//impl TypedFoo<Data2> for Data2..
fn main() {
let v: Vec<Box<Foo>> = vec![Box::new(Data(1))];
}
I can change Foo to this:
trait Foo {
fn f(&mut self);
fn get_self(&self) -> &Any;
}
Then get v[0].get_self() downcast_ref to Data, and then Data to &TypedFoo<Data>.
But is it possible to get &TypedFoo<Data> from &Foo without knowing "data type", some analog of Any but for a trait.
I imagine syntax like this:
let foo: &Foo = ...;
if let Some(typed_foo) = foo.cast::<Data>() {
}
My question is different from Can I cast between two traits?
because I have one generic and one ordinal trait. If I had two ordinal traits then the solution would be as simple as:
trait Foo {
fn f(&mut self);
fn as_typed_foo(&self) -> &TypedFoo;
}
Since TypedFoo is generic, none of the answers in that question help me. One possible solution could be:
trait Foo {
fn f(&mut self);
fn cast(&mut self, type_id: ::std::any::TypeId) -> Option<*mut ::std::os::raw::c_void>;
}
I am not sure how safe it is to cast *mut TypedFoo<T> -> *mut ::std::os::raw::c_void and then back to *mut TypedFoo<T>.
The signature of the function that you want is
fn convert<T>(x: &Box<Foo>) -> &TypedFoo<T>
To type check this signature compiler must know that the type inside Box implements TypedFoo<T> for some T. But conversion into trait-object erases information about the real type. Which means that it is impossible to statically type check signature of this function.
So we need to do it dynamically and if we want to use types a current crate doesn't know about, we'll need to resort to unsafe.
One option is to limit a set of types, which can be used in TypedFoo, and provide conversion functions in the Foo trait. This allows to avoid unsafe.
Playground link
Second option is to add to trait Foo a function, which returns a slice of pairs (TypeId, *const ()). The pointer is a type erased pointer to function, which does actual conversion. Conversion function searches required type identifier and executes corresponding function.
Playground link
For the sake of demonstration I used Vec instead of slice in conversion_registrar. But it shouldn't be too hard to change return type to &'static [(TypeId, *const ())], using lazy_static crate.
Is there a way to cast from one trait to another?
I have the traits Foo and Bar and a Vec<Box<dyn Foo>>. I know some of the items in the Vec implement the Bar trait, but is there any way I could target them?
I don't understand if this is possible or not.
trait Foo {
fn do_foo(&self);
}
trait Bar {
fn do_bar(&self);
}
struct SomeFoo;
impl Foo for SomeFoo {
fn do_foo(&self) {
println!("doing foo");
}
}
struct SomeFooBar;
impl Foo for SomeFooBar {
fn do_foo(&self) {
println!("doing foo");
}
}
impl Bar for SomeFooBar {
fn do_bar(&self) {
println!("doing bar");
}
}
fn main() {
let foos: Vec<Box<dyn Foo>> = vec![Box::new(SomeFoo), Box::new(SomeFooBar)];
for foo in foos {
foo.do_foo();
// if let Some(val) = foo.downcast_whatever::<Bar>() {
// val.bar();
// }
}
}
[Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8b637bddc4fc923ce705e84ad1d783d4)
No. There is no way to cast between two unrelated traits. To understand why, we have to understand how trait objects are implemented. To start with, let's look at TraitObject.
TraitObject is a reflection of how trait objects are actually implemented. They are composed of two pointers: data and vtable. The data value is just a reference to the original object:
#![feature(raw)]
use std::{mem, raw};
trait Foo {}
impl Foo for u8 {}
fn main() {
let i = 42u8;
let t = &i as &dyn Foo;
let to: raw::TraitObject = unsafe { mem::transmute(t) };
println!("{:p}", to.data);
println!("{:p}", &i);
}
vtable points to a table of function pointers. This table contains references to each implemented trait method, ordered by some compiler-internal manner.
For this hypothetical input
trait Foo {
fn one(&self);
}
impl Foo for u8 {
fn one(&self) { println!("u8!") }
}
The table is something like this pseudocode
const FOO_U8_VTABLE: _ = [impl_of_foo_u8_one];
A trait object knows a pointer to the data and a pointer to a list of methods that make up that trait. From this information, there is no way to get any other piece of data.
Well, almost no way. As you might guess, you can add a method to the vtable that returns a different trait object. In computer science, all problems can be solved by adding another layer of indirection (except too many layers of indirection).
See also:
Why doesn't Rust support trait object upcasting?
But couldn't the data part of the TraitObject be transmuted to the struct
Not safely, no. A trait object contains no information about the original type. All it has is a raw pointer containing an address in memory. You could unsafely transmute it to a &Foo or a &u8 or a &(), but neither the compiler nor the runtime data have any idea what concrete type it originally was.
The Any trait actually does this by also tracking the type ID of the original struct. If you ask for a reference to the correct type, the trait will transmute the data pointer for you.
Is there a pattern other than the one I described with my FooOrBar trait to handle such cases where we need to iterate over a bunch of trait objects but treat some of them slightly different?
If you own these traits, then you can add as_foo to the Bar trait and vice versa.
You could create an enum that holds either a Box<dyn Foo> or a Box<dyn Bar> and then pattern match.
You could move the body of bar into the body of foo for that implementation.
You could implement a third trait Quux where calling <FooStruct as Quux>::quux calls Foo::foo and calling <BarStruct as Quux>::quux calls Bar::foo followed by Bar::bar.
so... I don't think this is exactly what you want, but it's the closest I can get.
// first indirection: trait objects
let sf: Box<Foo> = Box::new(SomeFoo);
let sb: Box<Bar> = Box::new(SomeFooBar);
// second level of indirection: Box<Any> (Any in this case
// is the first Box with the trait object, so we have a Box<Box<Foo>>
let foos: Vec<Box<Any>> = vec![Box::new(sf), Box::new(sb)];
// downcasting to the trait objects
for foo in foos {
match foo.downcast::<Box<Foo>>() {
Ok(f) => f.do_foo(),
Err(other) => {
if let Ok(bar) = other.downcast::<Box<Bar>>() {
bar.do_bar();
}
}
}
}
note that we can call SomeFooBar as a Box<Bar> only because we stored it as a Box<Bar> in the first place. So this is still not what you want (SomeFooBar is a Foo too, but you can't convert it to a Box<Foo> any longer, so we're not really converting one trait to the other)
The short answer is: there is extremely limited support for downcasting at the moment in the language.
The long answer is that being able to downcast is not seen as high-priority for both technical and philosophical reasons:
from a technical stand-point, there are workarounds for most if not all situations
from a philosophical stand-point, downcasting leads to more brittle software (as you unexpectedly start relying on implementation details)
There have been multiple proposals, and I myself participated, but for now none has been selected and it is unclear whether Rust will ever get downcasting or if it does what its limitations will be.
In the mean time, you have essentially two workarounds:
Use TypeId: each type has an associated TypeId value which can be queried, then you can build a type-erased container such as Any and query whether the type it holds is a specific X. Behind the scenes Any will simply check the TypeId of this X against the TypeId of the value stored.
Create a specific trait, as you did.
The latter is more open-ended, and notably can be used with traits, whereas the former is limited to concrete types.
Here is what I did.
I added an as_bar method to the Foo trait that returns an Option<&Bar>. I gave the trait a default implementation to return None so that there is little to no inconvenience for Foo implementers that don't bother about Bar.
trait Foo {
fn do_foo(&self);
fn as_bar(&self) -> Option<&dyn Bar> {
None
}
}
I overwrite that method for the SomeFooBar struct which implements both Foo and Bar to return Some(self):
impl Foo for SomeFooBar {
fn do_foo(&self) {
println!("doing foo");
}
fn as_bar(&self) -> Option<&dyn Bar> {
Some(self)
}
}
Which makes the calling code look pretty much the way I want it to look.
fn main() {
let foos: Vec<Box<dyn Foo>> = vec![Box::new(SomeFoo), Box::new(SomeFooBar)];
for foo in foos {
foo.do_foo();
if let Some(bar) = foo.as_bar() {
bar.do_bar();
}
}
}
Playground
I would love to see Rust improve on this part in the future, but it's a solution I can totally live with for my case.
The only solution that I found originally is to introduce a third trait FooOrBar with explicit converter methods and implement that for both types. It doesn't feel like the right tool for the job though.
trait FooOrBar {
fn to_bar(&self) -> Option<&dyn Bar>;
fn to_foo(&self) -> Option<&dyn Foo>;
}
impl FooOrBar for SomeFooBar {
fn to_bar(&self) -> Option<&dyn Bar> {
Some(self)
}
fn to_foo(&self) -> Option<&dyn Foo> {
None
}
}
impl FooOrBar for SomeFoo {
fn to_bar(&self) -> Option<&dyn Bar> {
None
}
fn to_foo(&self) -> Option<&dyn Foo> {
Some(self)
}
}
fn main() {
let foos: Vec<Box<dyn FooOrBar>> = vec![Box::new(SomeFoo), Box::new(SomeFooBar)];
for foo in foos {
foo.to_foo().map(|foo| foo.do_foo());
foo.to_bar().map(|foo| foo.do_bar());
}
}
I was under the impression that these two things were only semantically different.
However, this is possible:
struct Foo;
trait Bar<T> {
fn resolve(&self) -> T;
}
impl Bar<isize> for Foo {
fn resolve(&self) -> isize {
return 0isize;
}
}
impl Bar<usize> for Foo {
fn resolve(&self) -> usize {
return 1usize;
}
}
#[test]
fn test_foo() {
let foo = Foo;
assert!((&foo as &Bar<isize>).resolve() == 0isize);
assert!((&foo as &Bar<usize>).resolve() == 1usize);
}
While this is not:
struct Foo;
trait Bar {
type T;
fn resolve(&self) -> Self::T;
}
impl Bar for Foo {
type T = isize;
fn resolve(&self) -> isize {
return 0isize;
}
}
impl Bar for Foo {
type T = usize;
fn resolve(&self) -> usize {
return 1usize;
}
}
#[test]
fn test_foo() {
let foo = Foo;
assert!((&foo as &Bar<T = isize>).resolve() == 0isize);
assert!((&foo as &Bar<T = usize>).resolve() == 1isize);
}
It generates:
<anon>:8:1: 13:2 error: conflicting implementations for trait `Bar` [E0119]
<anon>: 8 impl Bar for Foo {
<anon>: 9 type T = isize;
<anon>:10 fn resolve(&self) -> isize {
<anon>:11 return 0isize;
<anon>:12 }
<anon>:13 }
Am I missing something?
Is there a special syntax for what I'm trying to achieve, or is there really a... technical... distinction between a generic and an associated type?
Is there some circumstance in which an associated type has a tangible (rather than purely code prettiness) benefit over using a generic?
I'll repeat my comment: it is true that type parameters and associated types are only semantically different. However, that's the main point why they are both present in the language - they do their own separate jobs, so it is not "just" semantic difference, it is the whole reason for their existence as a separate thing from type parameters.
Note that I do not even touch syntactic differences. Of course it is absolutely natural that there are syntactic differences. These are separate features after all; if they had no syntactic differences, then how you would distinguish between them? Their syntactic difference is closely tied to the semantic difference, because the way associated types are defined makes it clear that they have "output" position, compared to "input" position of type parameters, but technically both type parameters and associated types (and also the implicit Self, parameter, by the way) are the same thing.
For anyone else who finds this question, there is also another technical distinction between type parameters and associated types as well.
If you attempt to implement a trait with an associated type you may see the error:
src/lib.rs:10:1: 15:2 error: the impl does not reference any types
defined in this crate; only traits defined in the current crate can
be implemented for arbitrary types [E0117]
If you have traits exported in a crate bar:
pub trait BarParam<TRtn> {
fn bar() -> TRtn;
}
pub trait BarAssoc {
type TRtn;
fn bar() -> Self::TRtn;
}
You will find that a crate importing these traits will only be able to implement:
impl<'a> BarParam<Foo> for &'a str {
fn bar() -> Foo {
return Foo;
}
}
While attempting to implement:
impl<'a> BarAssoc for &'a str {
type TRtn = Foo;
fn bar() -> Foo {
return Foo;
}
}
Will result in the error above.
Frankly I'm not really sure what's going on here, so if you do, by all means add a comment or another answer; but this is a tangible reason to avoid associated types when you're writing a crate.
Is it possible to declare an associated type that will represent a trait? If not, what can I do instead? Trying to do:
trait Foo {
/// A trait representing all types that can be returned from baz()
type ReturnType;
fn baz(&self) -> Self::ReturnType;
}
The biggest problem I'm running into is with the Sized trait, because I want a function that returns a vector of items of a type that implements ReturnType:
trait Foo {
type ReturnType;
// option 1
fn bar(&self) -> Vec<Self::ReturnType>;
// option 2
fn bar<T: Self::ReturnType>(&self) -> Vec<T>;
}
The problem with option 1 is that ReturnType won't be sized because it's a trait, and the problem with option 2 is the compiler won't recognize the associated type as a trait: failed to resolve. Use of undeclared type or module 'Self' and use of undeclared trait name 'Self::ReturnType' (which leads me to think associated types can't specify traits)
EDIT: An example of what I'm trying to do
/// Represents all types that store byte-data instead of the actual
/// element
trait BufferedVec {
/// the trait representing types that can be converted from the byte-data
type FromBuffer;
/// return the data at the given index, converted into a given type
fn get<T: Self::FromBuffer>(&self, index: usize) -> T;
}
A user's implementation might be
/// An implementation of a BufferedVec
struct MyBufferedVec<'a> {
data: &'a [Option<Vec<u8>>]
}
impl<'a> BufferedVec for MyBufferedVec<'a> {
type FromBuffer = MyFromTrait;
fn get<T: MyFromTrait>(&self, index: usize) -> T {
<T as MyFromTrait>::convert(self.data[index].as_ref())
}
}
trait MyFromTrait {
fn convert(val: Option<&[u8]>) -> Self;
}
impl MyFromTrait for i32 {
fn convert(val: Option<&[u8]>) -> i32 {
match val {
Some(ref bytes) => bytes[0] as i32,
None => 0
}
}
}
impl MyFromTrait for String {
fn convert(val: Option<&[u8]>) -> String {
match val {
Some(ref bytes) => String::from_utf8(bytes),
None => "".to_string()
}
}
}
Associated types cannot specify traits. You can't specify traits anywhere in Rust. You can require that a generic argument (or an associated type) needs to implement a trait.
trait Foo {
type ReturnType: Clone;
}
This way any implementors of Foo need to make sure that their choice of ReturnType also implements Clone.
impl Foo for Bar {
type ReturnType: i32;
}