Is it possible to have a collection of dynamic generic traits? - rust

I have a trait with generic type parameter. I want to put different objects that implements this trait in a collection. Object have different type parameters.
When I do it, compiler tells me that I need to specify generic type parameter. I actually don't need this generic type information for my case, so some kind of wildcard would work for me. Let me show the code, as it shows my intention better:
trait Test<T> {
fn test(&self) -> T;
}
struct Foo;
struct Bar;
impl Test<i64> for Foo {
fn test(&self) -> i64 {
println!("foo");
42
}
}
impl Test<String> for Bar {
fn test(&self) -> String {
println!("bar");
"".to_string()
}
}
fn main() {
// I'm not going to invoke test method which uses generic type parameter.
// So some kind of wildcard would work for me.
// But underscore is not wildcard and this does not compile.
let xs: Vec<Box<dyn Test<_>>> = vec![Box::new(Foo), Box::new(Bar)];
xs.iter().map(|x| {
// do some stuff, but not invoke Test::test() method, so I don't need type information
()
});
}
The error is:
error[E0277]: the trait bound `Bar: Test<i64>` is not satisfied
--> src/main.rs:24:57
|
24 | let xs: Vec<Box<dyn Test<_>>> = vec![Box::new(Foo), Box::new(Bar)];
| ^^^^^^^^^^^^^ the trait `Test<i64>` is not implemented for `Bar`
|
= help: the following implementations were found:
<Bar as Test<std::string::String>>
= note: required for the cast to the object type `dyn Test<i64>`
I understand why compiler gives me this error: I put Foo first and it has i64 as a type parameter. After that compiler expects only i64 as a type parameter. But is there a way to workaround this?

I think you can not make it work exactly like this.
Your options to achieve a similar result are to either have your elements implement another non generic trait that you then add to your Vec if you do not know ahead of time which types for T are ultimately possible i.e. the trait is part of your public API and other crates are expected to implement it for their own types T.
trait NonGenericTest {}
trait Test2<T> : NonGenericTest {
fn test(&self) -> T;
}
impl NonGenericTest for Foo{}
impl NonGenericTest for Bar{}
impl Test2<i64> for Foo {
fn test(&self) -> i64 {
println!("foo");
42
}
}
impl Test2<String> for Bar {
fn test(&self) -> String {
println!("bar");
"".to_string()
}
}
fn main() {
let xs: Vec<Box<dyn NonGenericTest>> = vec![Box::new(Foo), Box::new(Bar)];
xs.iter().map(|x| {
// do some stuff, but not invoke Test::test() method, so I don't need type information
()
});
}
Or if you know all possible types of T ahead of time you could change the T in your trait to an enum that contains all the types you want to support here:
enum TestResult {
ResultI64(i64),
ResultString(String),
}
trait Test {
fn test(&self) -> TestResult;
}
struct Foo;
struct Bar;
impl Test for Foo {
fn test(&self) -> TestResult {
println!("foo");
TestResult::ResultI64(42)
}
}
impl Test for Bar {
fn test(&self) -> TestResult {
println!("bar");
TestResult::ResultString("".to_string())
}
}
fn main(){
let xs: Vec<Box<dyn Test>> = vec![Box::new(Foo), Box::new(Bar)];
xs.iter().map(|x| {
// do some stuff, but not invoke Test::test() method, so I don't need type information
()
});
}

Related

Rust box a single trait that encompasses generic traits

I am in a situation where I want some objects to implement a trait, say "Base", and some other objects will implements a trait "Super". The Super trait also has to be generic for T : Base so that I can automatically implement parts of Super based on which Base it was specialized with. Now this seems to work fine with the following trivial example
trait Base {
fn say_hi() -> &'static str;
}
struct BaseOne {}
struct BaseTwo {}
impl Base for BaseOne {
fn say_hi() -> &'static str {
"hi!"
}
}
impl Base for BaseTwo {
fn say_hi() -> &'static str {
"hello!"
}
}
trait Super<T: Base> {
fn say_hi(&self) -> &'static str {
T::say_hi()
}
}
struct SuperOne;
struct SuperTwo;
impl Super<BaseOne> for SuperOne {}
impl Super<BaseTwo> for SuperTwo {}
My problem comes in with my next requirement, which is that I want to be able to store a vector of objects which implement Super, regardless of which Base it is specialized for. My idea for this is to create a trait that covers all Supers, such as the AnySuper trait below
trait AnySuper {
fn say_hi(&self) -> &'static str;
}
impl<T> AnySuper for dyn Super<T> where T : Base {
fn say_hi(&self) -> &'static str {
Super::say_hi(self)
}
}
And then store a vector of Box such as in the example below
fn main() {
let one = Box::new(SuperOne);
let two = Box::new(SuperTwo);
let my_vec: Vec<Box<dyn AnySuper>> = Vec::new();
my_vec.push(one);
my_vec.push(two);
}
But unfortunately that fails with the following error
error[E0277]: the trait bound `SuperOne: AnySuper` is not satisfied
--> src/main.rs:52:17
|
52 | my_vec.push(one);
| ^^^ the trait `AnySuper` is not implemented for `SuperOne`
|
= note: required for the cast to the object type `dyn AnySuper`
error[E0277]: the trait bound `SuperTwo: AnySuper` is not satisfied
--> src/main.rs:53:17
|
53 | my_vec.push(two);
| ^^^ the trait `AnySuper` is not implemented for `SuperTwo`
|
= note: required for the cast to the object type `dyn AnySuper`
Which is a bit strange because in my mind I have implemented AnySuper for all Super<T>. So my question is, am I doing something fundamentally wrong or is there just an issue with my syntax?
P.S. I have set up a playground with this code at https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a54e3e9044f1edaeb24d8ad934eaf7ec if anyone wants to play around with it.
I think something strange is happening since you're going through two layers of trait object indirection, i.e. from the concrete Box<SuperOne> to Box<dyn Super<BaseOne>> to Box<dyn AnySuper>. Certainly, Rust is equipped to handle the first case, as we do that all the time, but the second is not something I've seen.
From the sound of it, though, you want to say "AnySuper is implemented whenever Super is for any T", and what you've written in the code is "AnySuper is implemented for this one funny trait object type called dyn Super<T>". Let's try to write an actual blanket implementation.
impl<S, T> AnySuper for S where S : Super<T>, T : Base
But now we get some rather exciting error messages about unconstrained types.
error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
--> src/main.rs:36:9
|
36 | impl<S, T> AnySuper for S where S : Super<T>, T : Base {
| ^ unconstrained type parameter
You can read more about the motivation for this error on the relevant RFC, but the bottom line is that Rust can't figure out what T should be for some arbitrary S.
And that's true in general. If you give Rust some arbitrary thing that implements Super<T>, it might implement Super<T> for several different T, and then AnySuper would have to choose between those, which it can't do. Instead, we need to promise Rust that, for a given implementor, there's only one possible T, and we can do that by making T an associated type.
trait Super {
type T : Base;
fn say_hi(&self) -> &'static str {
Self::T::say_hi()
}
}
impl Super for SuperOne {
type T = BaseOne;
}
impl Super for SuperTwo {
type T = BaseTwo;
}
impl<S> AnySuper for S where S : Super, S::T : Base {
fn say_hi(&self) -> &'static str {
Super::say_hi(self)
}
}
Now Rust will happily accept your vector of AnySuper.
Rust Playground link
Working code example:
trait Base {
fn say_hi() -> &'static str;
}
struct BaseOne {}
struct BaseTwo {}
impl Base for BaseOne {
fn say_hi() -> &'static str {
"hi!"
}
}
impl Base for BaseTwo {
fn say_hi() -> &'static str {
"hello!"
}
}
trait Super {
type T : Base;
fn say_hi(&self) -> &'static str {
Self::T::say_hi()
}
}
struct SuperOne;
struct SuperTwo;
impl Super for SuperOne {
type T = BaseOne;
}
impl Super for SuperTwo {
type T = BaseTwo;
}
trait AnySuper {
fn say_hi(&self) -> &'static str;
}
impl<S> AnySuper for S where S : Super, S::T : Base {
fn say_hi(&self) -> &'static str {
Super::say_hi(self)
}
}
fn main() {
let one = Box::new(SuperOne);
let two = Box::new(SuperTwo);
let mut my_vec: Vec<Box<dyn AnySuper>> = Vec::new();
my_vec.push(one);
my_vec.push(two);
println!("Success!");
}

How can a Rust trait object return another trait object?

How can I attempt something like the following in Rust?
The builder class is a trait object which returns another trait object (type erasure) where the implementation that is selected is defined by the specific object of the builder trait that we are using.
trait Builder {
// I want this trait to return a trait object
fn commits(&self) -> dyn Commit;
fn finish(&self);
}
trait Commit {
}
struct FooBuilder {
}
struct FooCommit {
}
impl Builder for FooBuilder {
fn commits(&self) -> impl Commit {
FooCommit{ }
}
fn finish(&self) {
}
}
fn get_commits(b: &Builder) {
// trait object returns a trait
let c = b.commits();
}
fn main() {
let b = FooBuilder{};
get_commits(&b);
b.finish();
}
There is no problem returning trait objects from trait methods in Rust:
trait Foo {
fn bar(&self) -> Box<dyn Bar>;
}
One thing to notice is that you need to return Box<dyn Bar>, not dyn Bar, since size of dyn Bar is not known at compiled time, which renders it useless.
When you implement this trait, the signature has to match, so it should return Box<dyn Bar>, not impl Bar:
impl Foo for MyFoo {
fn bar(&self) -> Box<dyn Bar> {
Box::new(MyBar{})
}
}

Create a generic struct with Option<T> without specifying T when instantiating with None

I have a
struct Foo<T>
where
T: // ... some complex trait bound ...
{
a: Bar,
b: Option<T>,
}
When attempting to instantiate the struct with a b: None the compiler complains that it cannot infer the type and requires a type hint e.g. via the turbofish syntax. That is onerous on the caller because they will have to find a type that fulfills the trait bounds and import it despite not caring about that optional functionality.
I think what I am looking for would be a bottom type that automatically fulfills any trait bounds but cannot be instantiated so that None::<Bottom> could be used, but I have not found such a type in the documentation.
There's a feature in the works that allows specifying the never type as !. This is not present in stable Rust, so you need to use a nightly and a feature flag:
#![feature(never_type)]
fn thing<T>() -> Option<T> {
None
}
fn main() {
thing::<!>();
}
However, this doesn't work for your case yet (this is part of the reason that it's unstable):
#![feature(never_type)]
trait NothingImplementsMe {}
fn thing<T>() -> Option<T>
where T: NothingImplementsMe,
{
None
}
fn main() {
thing::<!>();
}
error[E0277]: the trait bound `!: NothingImplementsMe` is not satisfied
--> src/main.rs:12:5
|
12 | thing::<!>();
| ^^^^^^^^^^ the trait `NothingImplementsMe` is not implemented for `!`
|
= note: required by `thing`
The very first unresolved question on the tracking issue is:
What traits should we implement for !?
Since this feature is both unstable and doesn't do what you want, you may want to consider creating your own bespoke "bottom" type:
trait AlmostNothingImplementsMe {
fn foo();
}
struct Nope;
impl AlmostNothingImplementsMe for Nope {
fn foo() { unimplemented!() }
}
fn thing<T>() -> Option<T>
where T: AlmostNothingImplementsMe,
{
None
}
fn main() {
thing::<Nope>();
}
To improve the UX of this, I'd suggest creating a builder of some type that starts you off with the faux-bottom type:
mod nested {
pub trait AlmostNothingImplementsMe {
fn foo();
}
pub struct Nope;
impl AlmostNothingImplementsMe for Nope {
fn foo() { unimplemented!() }
}
pub fn with_value<T>(t: T) -> Option<T>
where T: AlmostNothingImplementsMe,
{
Some(t)
}
pub fn without_value() -> Option<Nope> {
None
}
}
fn main() {
nested::without_value();
}
You can see this similar pattern in crates like Hyper, although it boxes the concrete type so you don't see it from the outside.
One option to avoid the need to the turbofish operator is to have a type alias:
trait MyTrait {}
impl MyTrait for () {}
struct Foo<T: MyTrait> {
i: isize,
o: Option<T>,
}
type Bar = Foo<()>;
fn main() {
let foo_default = Bar { i: 1, o: None };
}
I used () as the default for simplicity, but ! (when available) or your own bottom type as in #Shepmaster's answer may be better.
A constructor function could also work if you don't mind Foo::new_default(i) or similar.

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.

Implementing Rust traits cause struct to not be found

When I implement a trait on a struct in Rust it's causing the struct type not to be found. First, the working code:
trait SomeTrait {
fn new() -> Box<SomeTrait>;
fn get_some_value(&self) -> int;
}
struct SomeStruct {
value: int
}
impl SomeStruct {
fn new() -> Box<SomeStruct> {
return box SomeStruct { value: 3 };
}
fn get_some_value(&self) -> int {
return self.value;
}
}
fn main() {
let obj = SomeStruct::new();
println!("{}", obj.get_some_value());
}
Here the SomeTrait trait isn't being used. Everything works. If I now change the impl of SomeStruct to implement SomeTrait:
trait SomeTrait {
fn new() -> Box<SomeTrait>;
fn get_some_value(&self) -> int;
}
struct SomeStruct {
value: int
}
impl SomeTrait for SomeStruct {
fn new() -> Box<SomeTrait> {
return box SomeStruct { value: 3 };
}
fn get_some_value(&self) -> int {
return self.value;
}
}
fn main() {
let obj = SomeStruct::new();
println!("{}", obj.get_some_value());
}
I get the error:
trait.rs:21:13: 21:28 error: failed to resolve. Use of undeclared module `SomeStruct`
trait.rs:21 let obj = SomeStruct::new();
^~~~~~~~~~~~~~~
trait.rs:21:13: 21:28 error: unresolved name `SomeStruct::new`.
trait.rs:21 let obj = SomeStruct::new();
What am I doing wrong? Why is SomeStruct suddenly missing? Thanks!
At the moment, associated functions (non-method functions) in traits are called via the trait, i.e. SomeTrait::new(). However, if you just write this, the compiler cannot work out which impl you're using, as there's no way to specify the SomeStruct information (it only works if the special Self type is mentioned in the signature somewhere). That is, the compiler needs to be able to work out which version of new should be called. (And this is required; they could have very different behaviour:
struct Foo;
impl SomeTrait for Foo {
fn new() -> Box<SomeTrait> { box Foo as Box<SomeTrait> }
}
struct Bar;
impl SomeTrait for Bar {
fn new() -> Box<SomeTrait> {
println!("hello")
box Bar as Box<SomeTrait>
}
}
Or something more dramatic than just printing.)
This is a language hole that will be filled by UFCS. For the moment, you need to use the dummy-Self trick:
trait SomeTrait {
fn new(_dummy: Option<Self>) -> Box<SomeTrait>;
...
}
which is then called like SomeTrait::new(None::<SomeStruct>).
However, I question why you are returning a boxed object from a constructor. This is rarely a good idea, it's normally better to just return the plain type directly, and the user can box it if necessary, that is,
trait SomeTrait {
fn new() -> Self;
...
}
(NB. this signature mentions Self and thus the Option trick above isn't required.)
Sidenote: the error message is rather bad, but it just reflects how these methods are implemented; an associated function in an impl Foo is very similar to writing mod Foo { fn ... }. You can see it differ by forcing the compiler to create that module:
struct Foo;
impl Foo {
fn bar() {}
}
fn main() {
Foo::baz();
}
prints just
<anon>:7:5: 7:13 error: unresolved name `Foo::baz`.
<anon>:7 Foo::baz();
^~~~~~~~
i.e. the Foo "module" exists.

Resources