Using Any with traits in Rust [duplicate] - rust

This question already has answers here:
Why doesn't Rust support trait object upcasting?
(5 answers)
Closed 6 years ago.
I'm trying to implement PartialEq in Rust for a trait that has subtypes, so that I can add them as boxed pointers to a container and later compare them.
Here's my scaled-down implementation:
use std::any::Any;
trait Foo: Any {}
struct Bar {}
impl Foo for Bar {}
struct Baz {}
impl Foo for Baz {}
impl PartialEq for Foo {
fn eq(&self, other: &Foo) -> bool {
let me = self as &Any;
let you = other as &Any;
if me.is::<Bar>() && you.is::<Bar>() {
true
} else if me.is::<Baz>() && you.is::<Baz>() {
true
} else {
false
}
}
}
fn main() {
let bar: Bar = Bar {};
let baz: Baz = Baz {};
let foo1: &Foo = &bar;
let foo2: &Foo = &baz;
println!("{:?}", foo1 == foo2);
}
Code example in Rust Playground.
When I build this, I get:
rustc 1.17.0-nightly (0648517fa 2017-02-03)
error: non-scalar cast: `&Foo + 'static` as `&std::any::Any + 'static`
--> <anon>:15:18
|
15 | let me = self as &Any;
| ^^^^^^^^^^^^
error: non-scalar cast: `&Foo + 'static` as `&std::any::Any + 'static`
--> <anon>:16:19
|
16 | let you = other as &Any;
| ^^^^^^^^^^^^^
error: aborting due to 2 previous errors
which is confusing. Any ideas what I'm doing wrong here?
Edit: I don't believe this is a duplicate of Why doesn't Rust support trait object upcasting?, because what I'm trying to do is downcast using Any, not upcast.
Further Edit: Yes, this is a duplicate - sorry, I was thinking about what I was trying to do (downcast to the Bar and Baz types) rather than how I was doing that (upcasting to Any). However, that being said, I guess I still don't understand why the Any example, where they do this: let value_any = value as &Any; works, where mine doesn't. That being said, Joshua Entrekin did give a great answer.
Final Edit An, never mind, it's because I'm upcasting a trait rather than a type - Doh!. Thanks, everyone!

This is a duplicate of Why doesn't Rust support trait object upcasting? because you are trying to upcast from Foo to Any. If you add an as_any method to Foo and implement on it, this code can be made to work:
use std::any::Any;
trait Foo: Any {
fn as_any(&self) -> &Any;
}
impl<T: Any> Foo for T {
fn as_any(&self) -> &Any {
self
}
}
struct Bar {}
struct Baz {}
impl PartialEq for Foo {
fn eq(&self, other: &Foo) -> bool {
let me = self.as_any();
let you = other.as_any();
if me.is::<Bar>() && you.is::<Bar>() {
true
} else if me.is::<Baz>() && you.is::<Baz>() {
true
} else {
false
}
}
}
fn main() {
let bar: Bar = Bar {};
let baz: Baz = Baz {};
let foo1: &Foo = &bar;
let foo2: &Foo = &baz;
println!("{:?}", foo1 == foo2);
}
I show it here in the Playground.

Related

What is an extension trait in Rust? [duplicate]

In Swift I can attach extension methods to any of struct, enum or protocol(same with trait in Rust).
protocol Foo1 {
func method1() -> Int
}
extension Foo1 {
func method2() {
print("\(method1())")
}
}
Then all types conforming the protocol Foo1 now all have method2().
This is very useful to build "method chaining" easily.
How to do same in Rust? This doesn't work with an error.
struct Kaz {}
impl Foo for Kaz {}
trait Foo {
fn sample1(&self) -> isize { 111 }
}
impl Foo {
fn sample2(&self) {
println!("{}", self.sample1());
}
}
fn main() {
let x = Kaz {};
x.sample1();
x.sample2();
}
Here's the error.
warning: trait objects without an explicit `dyn` are deprecated
--> src/main.rs:13:6
|
13 | impl Foo {
| ^^^ help: use `dyn`: `dyn Foo`
|
= note: `#[warn(bare_trait_objects)]` on by default
error[E0599]: no method named `sample2` found for type `Kaz` in the current scope
--> src/main.rs:22:7
|
3 | struct Kaz {}
| ---------- method `sample2` not found for this
...
22 | x.sample2();
| ^^^^^^^ method not found in `Kaz`
error: aborting due to previous error
In Rust, you can use extension traits, that is a trait with a generic implementation for all types T that implement the base trait:
struct Kaz {}
impl Foo for Kaz {}
trait Foo {
fn sample1(&self) -> isize { 111 }
}
trait FooExt {
fn sample2(&self);
}
impl<T: Foo> FooExt for T {
fn sample2(&self) {
println!("{}", self.sample1());
}
}
fn main() {
let x = Kaz {};
x.sample1();
x.sample2();
}
Playground

Why aren't traits necessary in scope when they are not used in a method-like style?

When I implement a trait for a given struct, I can obviously only use it by bringing the trait into scope. For example:
// file1.rs
pub struct MyStruct {
value: u64;
}
impl MyStruct {
pub fn new(value: u64) -> MyStruct {
MyStruct { value }
}
}
impl BitOr<MyStruct> for MyStruct {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self::new(self.value | rhs.value)
}
}
// main.rs
use file1::MyStruct;
let t1 = MyStruct::new(10u64);
let t2 = MyStruct::new(15u64);
let t3 = t1.bitor(t2); // This gives a compilation error unless std::ops::BitOr is in scope
The compiler has some useful warnings about this behaviour:
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
|
1 | use std::ops::BitOr;
|
However, if I replace let t3 = t1.bitor(t2); with let t3 = t1 | t2;, then the code compiles without errors even if std::ops::BitOr is not in scope. Why is that?

Extracting a function which creates an instance in Rust

A bit of a beginner Rust question - say I have the following code, which compiles:
trait XGetter {
fn get_x(&self) -> i32;
}
struct Foo {
x: i32
}
impl XGetter for Foo {
fn get_x(&self) -> i32 {
self.x
}
}
struct Bar<'a>(&'a dyn XGetter);
impl<'a> XGetter for Bar<'a> {
fn get_x(&self) -> i32 {
self.0.get_x()
}
}
fn baz() -> i32 {
let foo = Foo { x: 42 };
let bar = Bar(&foo);
bar.get_x()
}
Let's say I want to extract out the creation of Bar, in order encapsulate the creation of the XGetter and Bar together, such that baz() now reads:
fn baz2() -> i32 {
let bar = createBar(42);
bar.get_x()
}
However, by implementing createBar below, I run a-fowl of the borrow checker:
fn createBar<'a>(x: i32) -> Bar<'a> {
let foo = Foo { x };
let bar = Bar(&foo);
// ---- `foo` is borrowed here
bar
// ^^^ returns a value referencing data owned by the current function
}
How would one extract out a function createBar which doesn't break the borrowing rules?
The foo in createBar() dies when the function ends, so the bar you return would be pointing to invalid memory.
Given how you have written the call to createBar(42), it looks like you want Bar to own the Foo, so do that:
struct Bar(Box<dyn XGetter>);
impl XGetter for Bar {
fn get_x(&self) -> i32 {
self.0.get_x()
}
}
fn createBar(x: i32) -> Bar {
let foo = Box::new(Foo { x });
let bar = Bar(foo);
bar
}
You can not:
The signature struct Bar<'a>(&'a dyn XGetter); and createBar(i: 32) are incompatible. Because it means that in createBar you would have to instantiate an object implementing XGetter and that reference will not live outside of the scope of createBar.
fn createBar<'a>(x: i32) -> Bar<'a> {
let foo = Foo { x };
let bar = Bar(&foo);
// ---- `foo` is borrowed here
bar
// ^^^ returns a value referencing data owned by the current function
}
^^^ That means that the variable foo will live just during createBar scope.
That said, you could use:
fn createBar(g: &dyn XGetter) -> Bar<'_> {
Bar(g)
}
That way the reference will live outside of the scope of createBar.
Playground
As per the comments. If you want to abstract that, you need Bar to own Foo
struct Bar(Box<dyn XGetter>);
fn createBar(x: i32) -> Bar {
let foo = Box::new(Foo { x });
let bar = Bar(foo);
bar
}

How to implement bidirectional conversion with From trait in Rust?

Suppose I have the following type:
#[derive(Default)]
struct Foo(pub i32); // public
Since it's a tuple with a public member, any conversions from/to i32 can simply be made using the 0 member:
let foo = Foo::default();
let num: i32 = foo.0; // Foo to i32
let goo = Foo(123); // i32 to Foo
Now I want to make the 0 member non-public, implementing From trait:
#[derive(Default)]
struct Foo(i32); // non-public
impl From<i32> for Foo {
fn from(n: i32) -> Foo {
Foo(n)
}
}
But the conversion fails from i32 to Foo:
let foo = ay::Foo::default();
let num: i32 = foo.into(); // error!
let goo = ay::Foo::from(123); // okay
What's the correct way to implement this bidirectional conversion? Rust playground here.
You have to implement the other direction (impl From<Foo> for i32) manually:
mod x {
#[derive(Default)]
pub struct Foo(i32);
impl From<i32> for Foo {
fn from(n: i32) -> Foo {
Foo(n)
}
}
impl From<Foo> for i32 {
fn from(foo: Foo) -> i32 {
foo.0
}
}
}
fn main() {
let foo = x::Foo::default();
let _num: i32 = foo.into(); // okay
let _goo = x::Foo::from(123); // also okay
}
You can test this in the playground

Struct must outlive member reference

I'm not sure how to properly title this post. I'm fairly new to Rust and trying to compile a program following this simple structure, but it seems to be incorrect, and I'm not sure why.
struct Bar;
impl Bar {
fn do_thing(&self) { println!("Ha, do nothing!") }
}
struct Foo<'a> {
bar: &'a Bar
}
impl<'a> Foo<'a> {
fn new(b: &Bar) -> Foo { Foo { bar: b } }
fn get_bar(&self) -> &Bar { self.bar }
}
fn main() {
let b = Bar;
let b_ref = {
let f = Foo::new(&b);
f.get_bar()
};
b_ref.do_thing();
}
The compiler error here claims that f does not live long enough. It shouldn't matter how long f lives though -- b_ref is valid for as long as b is, and I thought that references were Copy so that we wouldn't have to worry about the actual reference in f. Is this relevant?
Part of me feels like I need to specify the lifetime of the &Bar being returned from get_bar, but I don't know how to tie that into the lifetime of the Foo struct?
What am I missing here?
You have to specify that the reference you are returning is tied to the lifetime 'a and not the lifetime of self that compiler will infer for you:
impl<'a> Foo<'a> {
fn new(b: &Bar) -> Foo { Foo { bar: b } }
fn get_bar(&self) -> &'a Bar { self.bar }
}
This is equivalent to the original code:
impl<'a> Foo<'a> {
fn new(b: &Bar) -> Foo { Foo { bar: b } }
fn get_bar<'b>(&'b self) -> &'b Bar { self.bar }
}
Part of me feel like I should need to specify the lifetime of the &Bar
being returned from get_bar, but I don't know how to tie that into the
lifetime of the Foo struct?
The lifetime of Foo does not matter at all in this case.

Resources