Two structures inherit one trait, but with extra parameters - rust

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.

Related

I don't understand "T::parse.parse(token_stream)" syntax in parse_macro_input macro of syn crate

#[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.

Automatically implement a trait for a type that already satisfies it

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!(); }

How to define a recursive trait bound in Rust?

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.

Default implementation of supertrait

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())
}
}

Is it possible to use `impl Trait` as a function's return type in a trait definition?

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.

Resources