Let's say I have the structs "F1" and "F2" that implement the Trait "Foo".
Now I want to write a function that accepts Foo and returns Bar.
trait Foo {
fn get_bar(&self) -> &Bar
}
fn do_match(f: &Foo) -> &Bar {
&match *f {
F1 => { f.get_bar() } // Error: mismatched types: expected `Foo`, found an enum or structure pattern
F2 => { f.get_bar().modify_somehow(3f64) }
}
}
Is it possible to match against structs implementing the trait Foo?
No, match cannot match on the concrete type of a value. Considering that match requires the patterns to be exhaustive, and that traits are open (types from other modules or other crates can implement your trait), you could never cover all cases (unless you have a simple variable binding like x on the last arm). match works better on enums, which are closed (the set of variants is fixed in the enum's definition).
You can introduce a second trait to do dynamic dispatch. Note that you can define the second trait and its implementations in a different module or a different crate from where F1 and F2 are defined.
trait Foo2 {
fn get_bar2(&self) -> &Bar;
}
impl Foo2 for F1 {
fn get_bar2(&self) -> &Bar {
self.get_bar()
}
}
impl Foo2 for F2 {
fn get_bar2(&self) -> &Bar {
self.get_bar().modify_somehow(3f64)
}
}
fn do_match(f: &Foo2) -> &Bar {
f.get_bar2()
}
Related
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.
Consider these two traits:
pub trait Foo {
fn new(arg: u32) -> Self;
}
pub trait Bar<P>: Foo {
fn with_parameter(arg: u32, parameter: P) -> Self;
}
I'd like to add the blanket impl:
impl<T: Bar<P>, P: Default> Foo for T {
fn new(arg: u32) -> Self {
Self::with_parameter(arg, P::default())
}
}
But I get the compiler error:
error[E0207]: the type parameter `P` is not constrained by the impl trait, self type, or predicates
--> src/lib.rs:9:17
|
9 | impl<T: Bar<P>, P: Default> Foo for T {
| ^ unconstrained type parameter
I think I get this error because I'm violating trait coherence rules, but I don't understand exactly what rule this would break. Why is this pattern not allowed? And, more importantly, can I achieve what I want without getting an error?
The problem is that a single type could implement Bar<P> for multiple values of P. If you had a struct Baz that implemented Bar<i32> and Bar<String>, which type should Foo::new use for P?
The only solution is to ensure that a single type cannot implement Bar more than once (if that's not what you want, then you have a flaw in your design!). To do so, we must replace the P type parameter with an associated type.
pub trait Bar: Foo {
type Parameter;
fn with_parameter(arg: u32, parameter: Self::Parameter) -> Self;
}
impl<T> Foo for T
where
T: Bar,
T::Parameter: Default,
{
fn new(arg: u32) -> Self {
Self::with_parameter(arg, T::Parameter::default())
}
}
An implementation of Bar would look like this:
struct Baz;
impl Bar for Baz {
type Parameter = i32;
fn with_parameter(arg: u32, parameter: Self::Parameter) -> Self {
unimplemented!()
}
}
See also:
Why do I get "the type parameter is not constrained" when creating a blanket implementation for a closure trait (Fn)?
I've broken down and extended Francis's explanation of why the code does not compile. I may not be the smartest kid on the block, but it took me way too long to understand his concise reasoning.
Let's create Baz, which implements Bar in 2 variants: i32 and String:
struct Baz;
impl Bar<i32> for Baz { /* ... */ }
impl Bar<String> for Baz { /* ... */ }
Type dependency graph after blanket impl takes effect:
-> trait Bar<i32> -> trait Foo (with i32 baked-in)
struct Baz
-> trait Bar<String> -> trait Foo (with String baked-in)
We end up with 2 different implementations of Foo: with baked-in i32 and with baked-in String.
When we write <Baz as Foo>::new(), compiler can't tell which version of Foo we mean; they are indistinguishable.
The rule of a thumb is that trait A can have blanket implementation for trait B only if trait A is generic over all generic parameters of trait B.
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.
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;
}