I have a trait, MyGoodTrait, with the function label(&self) -> &str. I want every implementor of MyGoodTrait to also implement Display and FromStr. However, I do not necessarily need Display and FromStr to be supertraits of MyGoodTrait. I would rather somehow have a default implementation of Display and FromStr, which will internally use the label function from MyGoodTrait. That way, every implementor of MyGoodTrait will get Display and FromStr "for free", as if there was a default implementation for those traits.
Here is an example that is similar to what I want to do, but it does not compile:
use std::str::FromStr;
pub trait MyGoodTrait {
fn new() -> Self;
fn label(&self) -> &'static str;
}
impl FromStr for dyn MyGoodTrait {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::new())
}
}
pub struct A {}
impl MyGoodTrait for A {
fn new() -> Self {
A{}
}
fn label(&self) -> &'static str {
"A"
}
}
pub struct B {}
impl MyGoodTrait for B {
fn new() -> Self {
B{}
}
fn label(&self) -> &'static str {
"B"
}
}
// In this hypothetical, A and B now both have `fmt` and `from_str` functions
Is there a way to write this default implementation of Display and FromStr, such that I do not have to duplicate the code for each struct that implements MyGoodTrait?
Note: My actual use case is that I have a trait which has serde::se::Serialize and serde::de::Deserialize as supertraits. The implementors of my trait will be used as keys in a map, and I will serialize the map to JSON, so I need the implementors to be serialized to Strings. So this may be an example of the XY Problem
TL;DR: You can't.
You can't implement FromStr for dyn SomeTrait because it has a method that returns a Result<Self, _>, hence you can only implement it for types whose size are know at compile time, which is not the case of trait objects.
What you would really want is
impl<T: MyGoodTrait> FromStr for T
But now you might violate the orphan rule. As the compiler explains:
Implementing a foreign trait is only possible if at least one of the types for which is it implemented is local.
Only traits defined in the current crate can be implemented for a type parameter.
But you could do it if FromStr was a local trait instead:
/// Copy of `std::str::FromStr`
trait Foo: Sized {
type Err;
fn from_str(s: &str) -> Result<Self, Self::Err>;
}
impl<T: MyGoodTrait> Foo for T {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::new())
}
}
Or you could implement it for any specific local type:
impl FromStr for A {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::new())
}
}
Related
#[doc(hidden)]
pub fn parse<T: ParseMacroInput>(token_stream: TokenStream) -> Result<T> {
T::parse.parse(token_stream)
} // Not public API.
#[doc(hidden)]
pub trait ParseMacroInput: Sized {
fn parse(input: ParseStream) -> Result<Self>;
}
impl<T: Parse> ParseMacroInput for T {
fn parse(input: ParseStream) -> Result<Self> {
<T as Parse>::parse(input)
}
}
I guess that the code you find weird is that in the title:
T::parse.parse(token_stream)
Here T is a generic type that implements ParseMacroInput, because of the constraint T: ParseMacroInput above. The only member of that trait is that fn parse(input: ParseStream) -> Result<Self>; function, so T::parse must be a reference to that function.
Now, what about the next .parse? Well functions are values too, and they can implement traits. And indeed in synthere is this trait:
pub trait Parser: Sized {
// ...
fn parse(self, tokens: proc_macro::TokenStream) -> Result<Self::Output> { ... }
}
and an implementation for functions that take a ParseStream and returns a Result<_>, just like function ParseMacroInput::parse.
impl<F, T> Parser for F
where
F: FnOnce(ParseStream) -> Result<T>
And that is the function your original T::parse.parse() calls: a function of a trait implemented by a function type or another generic type. The fact that both functions have the same name is an unfortunate coincidence.
How do I make two structures inherit one trait, but with extra parameters?
And I heard that doing so in Rust is not recommended. Why?
struct Foo {name: String,}
struct FooPlus {name: String, lvl: i128,}
trait Trait {
fn show(&self);
fn new(name: &str) -> Self;
// fn new(name: &str, lvl: i128) -> Self;
}
impl Trait for Foo {
fn new(name: &str) -> Self {}
fn show(&self) {}
}
impl Trait for FooPlus {
fn show(&self) {}
fn new(name: &str, lvl: i128) -> Self {}
}
You can make the extra parameter be an associated type to the trait.
trait Trait {
type ConstructParam
fn show(&self);
fn new(name: &str, arg: ConstructParam) -> Self;
}
impl Trait for Foo {
type ConstructParam = ();
fn show(&self) { ... }
fn new(name: &str, arg: ()) -> Self { ... }
}
impl Trait for FooPlus {
type ConstructParam = i128;
fn show(&self) { ... }
fn new(name: &str, arg: i128) -> Self { ... }
}
Note that you have to call the "no-argument" constructor with an extra unit argument, so Foo::new("example name", ()), but that unit argument is zero-sized and thus will almost certainly be optimized to nothing.
Though, as already mentioned in the comments, having new in a trait is odd. How often are you going to polymorphically call new on a type that's generic? If you really need to polymorphically generate instances, that sounds like an application of the factory pattern, not a good use for traits in this way. And if you're not doing it polymorphically, then just make new part of the impl for Foo and FooPlus separately.
First, I know I can use Box if I want to define a recursive structure. For example,
struct LinkNode {
next: Option<Box<LinkNode>>
}
impl LinkNode{
fn get_next(&self) -> Option<Box<LinkNode>>{
None
}
fn append_next(&mut self, next: LinkNode) -> Self{
self
}
}
But how can I make a trait on these structures via templates or trait object?
Due to the existence of fn append_next(...) -> Self, I cannot create a trait object directly like this:
pub trait Linkable {
fn get_next(&self) -> Option<Box<dyn Linkable>>;
fn append_next(&mut self, next: impl Linkable) -> Self;
}
And we cannot return Option<Box<impl Linkable>> or impl Linkable for fn get_next(&self).
Then I tried the following implementation via generic templates and it does not work.
Because I need to assign the type of T recursively when construct a new LinkNode.
pub trait Linkable<T:Linkable<T> + Clone> : Clone {
fn get_next(&self) -> Option<Box<T>>;
fn append_next(&mut self, next: T) -> Self;
}
I finally implement it in this way, by creating other traits for assistance. And it works well. Again...Is there other better ways?
pub trait Linkable: LinkClone{
fn get_next(&self) -> Option<Box<dyn Linkable>>;
}
pub trait LinkAppend {
fn append_next(&mut self, next: Box<dyn Linkable>) -> Box<dyn Linkable>;
}
pub trait LinkClone{
fn clone_box(&self) -> Box<dyn Linkable>;
}
impl<T> LinkClonefor T
where
T: 'static + Linkable+ LinkAppend + Clone,
{
fn clone_box(&self) -> Box<dyn Linkable> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn Linkable> {
fn clone(&self) -> Box<dyn Linkable> {
self.clone_box()
}
}
BTW, I have some other questions during the exploration above: Why Rust forbids the impl Linkable sugar, like the Box<impl Linkale>? And why returning impl Linkable is forbidden in a trait?
Updated after Ibraheem's answer:
Except the associated type implementation from Ibraheem, it is also fine to work like this. The core idea is to avoid the recursive type declaration in the trait.
pub trait Linkable {
fn get_next<T:Linkable>(&self) -> Next<T>;
fn append_next<T:Linkable>(&mut self, next: Next<T>) -> Self;
}
struct Next<T: Linkable> {
node: T,
}
This is mentioned in another question: Can I define a trait with a type parameter of itself in Rust?
Linkable could have associated type called Next.
pub trait Linkable {
type Next: Linkable;
}
get_next now returns an instance of type Self::Next, and append_next takes Self::Next as a parameter:
pub trait Linkable {
type Next: Linkable;
fn get_next(&self) -> Option<Self::Next>;
fn append_next(&mut self, next: Self::Next) -> &Self;
}
Now you can implement Linkable for Linknode:
impl Linkable for LinkNode {
type Next = LinkNode;
fn get_next(&self) -> Option<Box<LinkNode>> {
None
}
fn append_next(&mut self, next: LinkNode) -> &Self {
self
}
}
Why Rust forbids the impl Linkable sugar, like the Box? And why returning impl Linkable is forbidden in a trait?
You can refer to Is it possible to use impl Trait as a function's return type in a trait definition? for the answer to this question.
Is it at all possible to define functions inside of traits as having impl Trait return types? I want to create a trait that can be implemented by multiple structs so that the new() functions of all of them returns an object that they can all be used in the same way without having to write code specific to each one.
trait A {
fn new() -> impl A;
}
However, I get the following error:
error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
--> src/lib.rs:2:17
|
2 | fn new() -> impl A;
| ^^^^^^
Is this a limitation of the current implementation of impl Trait or am I using it wrong?
As trentcl mentions, you cannot currently place impl Trait in the return position of a trait method.
From RFC 1522:
impl Trait may only be written within the return type of a freestanding or inherent-impl function, not in trait definitions or any non-return type position. They may also not appear in the return type of closure traits or function pointers, unless these are themselves part of a legal return type.
Eventually, we will want to allow the feature to be used within traits [...]
For now, you must use a boxed trait object:
trait A {
fn new() -> Box<dyn A>;
}
See also:
Is it possible to have a constructor function in a trait?
Why can a trait not construct itself?
How do I return an instance of a trait from a method?
Nightly only
If you wish to use unstable nightly features, you can use existential types (RFC 2071):
// 1.67.0-nightly (2022-11-13 e631891f7ad40eac3ef5)
#![feature(type_alias_impl_trait)]
#![feature(return_position_impl_trait_in_trait)]
trait FromTheFuture {
type Iter: Iterator<Item = u8>;
fn returns_associated_type(&self) -> Self::Iter;
// Needs `return_position_impl_trait_in_trait`
fn returns_impl_trait(&self) -> impl Iterator<Item = u16>;
}
impl FromTheFuture for u8 {
// Needs `type_alias_impl_trait`
type Iter = impl Iterator<Item = u8>;
fn returns_associated_type(&self) -> Self::Iter {
std::iter::repeat(*self).take(*self as usize)
}
fn returns_impl_trait(&self) -> impl Iterator<Item = u16> {
Some((*self).into()).into_iter()
}
}
fn main() {
for v in 7.returns_associated_type() {
println!("type_alias_impl_trait: {v}");
}
for v in 7.returns_impl_trait() {
println!("return_position_impl_trait_in_trait: {v}");
}
}
If you only need to return the specific type for which the trait is currently being implemented, you may be looking for Self.
trait A {
fn new() -> Self;
}
For example, this will compile:
trait A {
fn new() -> Self;
}
struct Person;
impl A for Person {
fn new() -> Person {
Person
}
}
Or, a fuller example, demonstrating using the trait:
trait A {
fn new<S: Into<String>>(name: S) -> Self;
fn get_name(&self) -> String;
}
struct Person {
name: String
}
impl A for Person {
fn new<S: Into<String>>(name: S) -> Person {
Person { name: name.into() }
}
fn get_name(&self) -> String {
self.name.clone()
}
}
struct Pet {
name: String
}
impl A for Pet {
fn new<S: Into<String>>(name: S) -> Pet {
Pet { name: name.into() }
}
fn get_name(&self) -> String {
self.name.clone()
}
}
fn main() {
let person = Person::new("Simon");
let pet = Pet::new("Buddy");
println!("{}'s pets name is {}", get_name(&person), get_name(&pet));
}
fn get_name<T: A>(a: &T) -> String {
a.get_name()
}
Playground
As a side note.. I have used String here in favor of &str references.. to reduce the need for explicit lifetimes and potentially a loss of focus on the question at hand. I believe it's generally the convention to return a &str reference when borrowing the content and that seems appropriate here.. however I didn't want to distract from the actual example too much.
You can get something similar even in the case where it's not returning Self by using an associated type and explicitly naming the return type:
trait B {}
struct C;
impl B for C {}
trait A {
type FReturn: B;
fn f() -> Self::FReturn;
}
struct Person;
impl A for Person {
type FReturn = C;
fn f() -> C {
C
}
}
Fairly new to Rust, so may need checking.
You could parametrise over the return type. This has limits, but they're less restrictive than simply returning Self.
trait A<T> where T: A<T> {
fn new() -> T;
}
// return a Self type
struct St1;
impl A<St1> for St1 {
fn new() -> St1 { St1 }
}
// return a different type
struct St2;
impl A<St1> for St2 {
fn new() -> St1 { St1 }
}
// won't compile as u32 doesn't implement A<u32>
struct St3;
impl A<u32> for St3 {
fn new() -> u32 { 0 }
}
The limit in this case is that you can only return a type T that implements A<T>. Here, St1 implements A<St1>, so it's OK for St2 to impl A<St2>. However, it wouldn't work with, for example,
impl A<St1> for St2 ...
impl A<St2> for St1 ...
For that you'd need to restrict the types further, with e.g.
trait A<T, U> where U: A<T, U>, T: A<U, T> {
fn new() -> T;
}
but I'm struggling to get my head round this last one.
Suppose the following objects:
pub struct MyStruct<T>{
items: Vec<T>
}
pub impl<T> MyStruct<T> {
pub fn new() -> MyStruct {
MyStruct{ items::new(); }
}
pub fn add(&mut self, item: T) where T : Eq {
self.items.push(item);
}
}
pub trait C {}
pub struct Another {
mystruct: MyStruct<Box<C>>
}
pub impl Another {
pub fn exec<C>(&mut self, c: C) where C: Eq + C {
self.mystruct.add(c);
}
}
On exec I'm enforcing that C also is Eq, but I'm receiving the following error:
error: the trait `core::cmp::Eq` is not implemented for the type `C`
I had to make
pub impl<T> MyStruct<T>
instead of
pub impl<T : Eq> MyStruct<T>
because since C is a trait I cant enforce Eq when using MyStruct::new, so I left the check for the type guard on the function. What's happening here?
Let’s look at the relevant definitions for Eq:
pub trait Eq: PartialEq<Self> {
…
}
pub trait PartialEq<Rhs: ?Sized = Self> {
fn eq(&self, other: &Rhs) -> bool;
…
}
Now consider MyStruct<Box<C>>: the type that it wants to implement Eq is Box<C>, a boxed trait object. To implement Eq, Box<C> must first implement PartialEq<Box<C>>, like this:
impl PartialEq for Box<C> {
fn eq(&self, other: &Box<C>) -> bool;
}
impl Eq for Box<C> { }
That is, you must be able to compare an arbitrary Box<C> with any other arbitrary Box<C>. The boxed trait objects that you are comparing could be of different concrete types, here. Thus you need to write this implementation manually. In such cases, you will typically want the trait to include some way of normalising the form of the object into a concrete, comparable type; for some types this is obvious; if AsRef<T> was to have a PartialEq implementation added, the implementation to add would be fairly obvious:
impl<T: PartialEq> PartialEq for AsRef<T> {
fn eq(&self, other: &AsRef<T>) -> bool {
self.as_ref() == other.as_ref()
}
}
(Note also that if C implements PartialEq, Box<C> does, so such implementations as we’re discussing should go on the unboxed trait object, not on the boxed trait object.)
Quite often, however, there is not an obvious and simple implementation. There are a few approaches you could take:
Convert the objects (potentially expensively, though ideally cheaply) into some base type, e.g. a String which can then be compared;
Give up;
Constrain C to 'static types and use some fancy Any magic to make it so that it uses the base type’s PartialEq implementation, returning false if the two trait objects are not of the same concrete type. This one is a fairly useful one, so I’ll give some code for it:
#![feature(core)]
use std::any::{Any, TypeId};
use std::mem;
fn main() { }
trait PartialEqFromC {
fn eq_c(&self, other: &C) -> bool;
}
impl<T: PartialEq + Any + C> PartialEqFromC for T {
fn eq_c(&self, other: &C) -> bool {
if other.get_type_id() == TypeId::of::<Self>() {
self == unsafe { *mem::transmute::<&&C, &&Self>(&other) }
} else {
false
}
}
}
trait C: Any + PartialEqFromC {
}
impl PartialEq for C {
fn eq(&self, other: &C) -> bool {
self.eq_c(other)
}
}
Note that this example depends on the unstable feature core for Any.get_type_id and is thus tied to nightly only; this can be worked around by duplicating that definition from the Any trait into a new supertrait of C and could also be simplified by mopafying the C trait.