Option<T> where T can be two different traits? - traits

If I have two different traits:
trait Foo {}
trait Bar {}
is it then possible to have a Option which can be either of them (or None, of course), like:
struct FooBar {
fb: Option<~Foo or Bar>
}
let fb1 = FooBar{fb: Some(~somestruct as ~Foo)}
let fb2 = FooBar{fb: Some(~otherstruct as ~Bar)}
and have them both work?

There is nothing special about Option<T>; it is simply this, plus some convenience methods and a bit of documentation:
pub enum Option<T> {
Some(T),
None,
}
Bear this in mind: it's enums all the way. You can make your own enums. It will certainly be explicit, but learn to appreciate that as a good thing.
pub enum FooOrBar {
Foo(~Foo),
Bar(~Bar),
}
You can make an Option<T> of this. Or perhaps you would prefer to blend the two, if you can get better semantic meaning out of it:
pub enum Something {
Foozy(~Foo),
Bark(~Bar),
ADifferentVariant,
}

Related

is `as_ref` rust's ways to implement idiomatic generic?

As I understand it, rust is a "has a" and not a "is a" language (composition over inheritance).
this makes Liskov substitutions slightly more complicated but not impossible using Traits. While I can use LSP, it appears to not be the idiomatic way of implementing type coercion in rust. I'm left confused of how to operate.
minimal example
Let's assume I have two structs
struct Real(i32);
struct Complex(Real, Real);
And a function project which takes a Complex and return a projection of the input.
#[derive(Clone, Copy)]
struct Real(i32);
struct Complex(Real, Real);
// we pass by reference because we need to be blazingly fast
fn project(c : &Complex) -> Complex {
Complex(c.0, Real(0))
}
fn main() {
let a = Complex(Real(1), Real(2));
let x = project(&a);
println!("{} + {}i", x.0.0, x.1.0)
}
To keep things simple, please assume we are the situation in which we benefit from passing Real by reference and project should not be duplicated as multiple implementation from a trait for Real and Complex.
Assume we expect to also use project on Reals from time to time.
Making project somewhat generic
My OOP instincts pushes me to make some supertype for Real and Complex, let's say the trait AsReal
#[derive(Clone, Copy)]
struct Real(i32);
struct Complex(Real, Real);
trait AsReal {
fn as_real(&self) -> Real;
}
impl AsReal for Real { fn as_real(&self) -> Real { *self } }
impl AsReal for Complex { fn as_real(&self) -> Real { self.0 } }
fn project (r : &impl AsReal) -> Complex {
Complex( r.as_real(), Real(0) )
}
fn main() {
let a = Real(1);
let b = Complex(Real(2), Real(3));
let x = project(&a);
let y = project(&b);
println!("{} + {}i", x.0.0, x.1.0);
println!("{} + {}i", y.0.0, y.1.0);
}
But apparently, the rusty way would be to use AsRef<Real> instead
#[derive(Clone, Copy)]
struct Real(i32);
struct Complex(Real, Real);
fn project<U: AsRef <Real>>(r : U) -> Complex {
Complex ( *r.as_ref(), Real(0) )
}
impl AsRef<Real> for Complex {
fn as_ref(&self) -> &Real { &self.0 }
}
impl AsRef<Real> for Real {
fn as_ref(&self) -> &Real { self }
}
fn main() {
let a = Real(1);
let b = Complex(Real(2), Real(3));
let x = project(&a);
let y = project(&b);
println!("{} + {}i", x.0.0, x.1.0);
println!("{} + {}i", y.0.0, y.1.0);
}
Which leaves me unsatisfied : the prototype for project became very wordy and hard to read. So much so it feels like the convenience of use for project is simply not worth it.
Furthermore, it means the function must opt-in for Complex into Real coercion and I dislike that notion as it feel like it pushes me to develop defensively and use AsRef<...> all the time.
I don't feel like I have the full picture, what would be the idiomatic way to interact with rust for situation like this ?
Based on your description, it seems like you could go with this:
project takes a Real
Complex provides an into_real() method that returns a Real
Small sidenote: if your types are small and Copy, a pointer isn't always faster. Compiler explorer can be a great tool for showing you what the assembly for a snippet is/
That being said, I'd write it like this.
fn project(real: Real) -> Real {
// very complex logic
}
// deriving Copy makes these types behave more like numbers
#[derive(Copy, Clone)]
struct Real(i32);
#[derive(Copy, Clone)]
struct Complex(Real, Real);
impl Complex {
fn into_real(self) -> Real {
self.0
}
}
fn main() {
let real = Real(0);
let complex = Complex(Real(0), Real(0));
project(real);
project(complex.into_real());
}
If you really hate having to write into_real(), you could make the call-site simpler and make the declaration-site more complex by:
implementing From<Complex> for Real (though arguably this needs its own trait since there's more than one way to get a Real from a Complex)
making project accept an impl Into<Real>
impl From<Complex> for Real {
fn from(c: Complex) {
c.0
}
}
fn project(real: impl Into<Real>) {
let real = real.into();
// real is a `Real` here
}
Though honestly, I'd just go for the first option. Your function doesn't really need to be generic, and that increases monomorphization cost. It's not very OOP, but Rust is not an OOP language.

How can I test that two structs have the same value without cloning?

I write tests in other languages where I explicitly define expected and actual values. In Rust, I was running into lifetime errors because my test code creates a struct that uses a value from another struct.
Two ways I have found to avoid this is to:
use references with the struct type defined with a pointer
use cloning.
I have seen many posts asking how to avoid excessive cloning so I wonder if there is a more idiomatic way of doing this.
Can I get rid of the clone in the following test:
#[derive(Clone, Debug, PartialEq)]
struct SomethingElse {
a: u16,
}
struct Something {
inner: SomethingElse,
}
impl Something {
pub fn do_something(&self) -> SomethingElse {
return self.inner.clone();
}
}
mod tests {
#[cfg(test)]
use super::*;
#[test]
fn something_does_something() {
let expected = SomethingElse { a: 1 };
let something = Something {
inner: expected.clone(),
};
assert_eq!(something.do_something(), expected);
}
}
In any programming language, "best practices" for production code are very different from best practices for tests. In particular, you probably don't much care about performance in tests. The most important thing is that someone reading the test can see what it does and that it doesn't contain too much noise or indirection that distracts from what the test is actually testing.
If you need to assert that a field of one struct contains the same data as some field of another struct, cloning seems like exactly the right thing to do. In your case, since it's simple, you might even consider duplicating the code that describes the inner struct:
#[test]
fn something_does_something() {
let something = Something {
inner: SomethingElse { a: 1 },
};
assert_eq!(something.do_something(), SomethingElse { a: 1 });
}
I'm not saying that this is idiomatic, or wise in this particular case, but you can avoid cloning by comparing through a reference. Instead of
let expected = SomethingElse { a: 1 };
let something = Something {
inner: expected.clone(),
};
assert_eq!(something.do_something(), expected);
write
let something = Something {
inner: SomethingElse { a: 1 },
};
let expected = &something.inner;
assert_eq!(&something.do_something(), expected);
I would only do this if it is actually important to the properties being tested that the value not be cloned (perhaps because its impl Clone does something special).

How do you define custom `Error` types in Rust?

I'm writing a function that could return several one of several different errors.
fn foo(...) -> Result<..., MyError> {}
I'll probably need to define my own error type to represent such errors. I'm presuming it would be an enum of possible errors, with some of the enum variants having diagnostic data attached to them:
enum MyError {
GizmoError,
WidgetNotFoundError(widget_name: String)
}
Is that the most idiomatic way to go about it? And how do I implement the Error trait?
You implement Error exactly like you would any other trait; there's nothing extremely special about it:
pub trait Error: Debug + Display {
fn description(&self) -> &str { /* ... */ }
fn cause(&self) -> Option<&Error> { /* ... */ }
fn source(&self) -> Option<&(Error + 'static)> { /* ... */ }
}
description, cause, and source all have default implementations1, and your type must also implement Debug and Display, as they are supertraits.
use std::{error::Error, fmt};
#[derive(Debug)]
struct Thing;
impl Error for Thing {}
impl fmt::Display for Thing {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Oh no, something bad went down")
}
}
Of course, what Thing contains, and thus the implementations of the methods, is highly dependent on what kind of errors you wish to have. Perhaps you want to include a filename in there, or maybe an integer of some kind. Perhaps you want to have an enum instead of a struct to represent multiple types of errors.
If you end up wrapping existing errors, then I'd recommend implementing From to convert between those errors and your error. That allows you to use try! and ? and have a pretty ergonomic solution.
Is that the most idiomatic way to go about it?
Idiomatically, I'd say that a library will have a small (maybe 1-3) number of primary error types that are exposed. These are likely to be enumerations of other error types. This allows consumers of your crate to not deal with an explosion of types. Of course, this depends on your API and whether it makes sense to lump some errors together or not.
Another thing to note is that when you choose to embed data in the error, that can have wide-reaching consequences. For example, the standard library doesn't include a filename in file-related errors. Doing so would add overhead to every file error. The caller of the method usually has the relevant context and can decide if that context needs to be added to the error or not.
I'd recommend doing this by hand a few times to see how all the pieces go together. Once you have that, you will grow tired of doing it manually. Then you can check out crates which provide macros to reduce the boilerplate:
error-chain
failure
quick-error
Anyhow
SNAFU
My preferred library is SNAFU (because I wrote it), so here's an example of using that with your original error type:
use snafu::prelude::*; // 0.7.0
#[derive(Debug, Snafu)]
enum MyError {
#[snafu(display("Refrob the Gizmo"))]
Gizmo,
#[snafu(display("The widget '{widget_name}' could not be found"))]
WidgetNotFound { widget_name: String },
}
fn foo() -> Result<(), MyError> {
WidgetNotFoundSnafu {
widget_name: "Quux",
}
.fail()
}
fn main() {
if let Err(e) = foo() {
println!("{}", e);
// The widget 'Quux' could not be found
}
}
Note I've removed the redundant Error suffix on each enum variant. It's also common to just call the type Error and allow the consumer to prefix the type (mycrate::Error) or rename it on import (use mycrate::Error as FooError).
1 Before RFC 2504 was implemented, description was a required method.
The crate custom_error allows the definition of custom error types with less boilerplate than what was proposed above:
custom_error!{MyError
Io{source: io::Error} = "input/output error",
WidgetNotFoundError{name: String} = "could not find widget '{name}'",
GizmoError = "A gizmo error occurred!"
}
Disclaimer: I am the author of this crate.
Is that the most idiomatic way to go about it? And how do I implement the Error trait?
It's a common way, yes. "idiomatic" depends on how strongly typed you want your errors to be, and how you want this to interoperate with other things.
And how do I implement the Error trait?
Strictly speaking, you don't need to here. You might for interoperability with other things that require Error, but since you've defined your return type as this enum directly, your code should work without it.

Why doesn't Rust support trait object upcasting?

Given this code:
trait Base {
fn a(&self);
fn b(&self);
fn c(&self);
fn d(&self);
}
trait Derived : Base {
fn e(&self);
fn f(&self);
fn g(&self);
}
struct S;
impl Derived for S {
fn e(&self) {}
fn f(&self) {}
fn g(&self) {}
}
impl Base for S {
fn a(&self) {}
fn b(&self) {}
fn c(&self) {}
fn d(&self) {}
}
Unfortunately, I cannot cast &Derived to &Base:
fn example(v: &Derived) {
v as &Base;
}
error[E0605]: non-primitive cast: `&Derived` as `&Base`
--> src/main.rs:30:5
|
30 | v as &Base;
| ^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
Why is that? The Derived vtable has to reference the Base methods in one way or another.
Inspecting the LLVM IR reveals the following:
#vtable4 = internal unnamed_addr constant {
void (i8*)*,
i64,
i64,
void (%struct.S*)*,
void (%struct.S*)*,
void (%struct.S*)*,
void (%struct.S*)*
} {
void (i8*)* #_ZN2i813glue_drop.98717h857b3af62872ffacE,
i64 0,
i64 1,
void (%struct.S*)* #_ZN6S.Base1a20h57ba36716de00921jbaE,
void (%struct.S*)* #_ZN6S.Base1b20h3d50ba92e362d050pbaE,
void (%struct.S*)* #_ZN6S.Base1c20h794e6e72e0a45cc2vbaE,
void (%struct.S*)* #_ZN6S.Base1d20hda31e564669a8cdaBbaE
}
#vtable26 = internal unnamed_addr constant {
void (i8*)*,
i64,
i64,
void (%struct.S*)*,
void (%struct.S*)*,
void (%struct.S*)*,
void (%struct.S*)*,
void (%struct.S*)*,
void (%struct.S*)*,
void (%struct.S*)*
} {
void (i8*)* #_ZN2i813glue_drop.98717h857b3af62872ffacE,
i64 0,
i64 1,
void (%struct.S*)* #_ZN9S.Derived1e20h9992ddd0854253d1WaaE,
void (%struct.S*)* #_ZN9S.Derived1f20h849d0c78b0615f092aaE,
void (%struct.S*)* #_ZN9S.Derived1g20hae95d0f1a38ed23b8aaE,
void (%struct.S*)* #_ZN6S.Base1a20h57ba36716de00921jbaE,
void (%struct.S*)* #_ZN6S.Base1b20h3d50ba92e362d050pbaE,
void (%struct.S*)* #_ZN6S.Base1c20h794e6e72e0a45cc2vbaE,
void (%struct.S*)* #_ZN6S.Base1d20hda31e564669a8cdaBbaE
}
All Rust vtables contain a pointer to the destructor, size and alignment in the first fields, and the subtrait vtables don't duplicate them when referencing supertrait methods, nor use indirect reference to supertrait vtables. They just have copies of the method pointers verbatim and nothing else.
Given that design, it's easy to understand why this does not work. A new vtable would need to be constructed at runtime, which would likely reside on the stack, and that isn't exactly an elegant (or optimal) solution.
There are some workarounds, of course, like adding explicit upcast methods to the interface, but that requires quite a bit of boilerplate (or macro frenzy) to work properly.
Now, the question is - why isn't it implemented in some way that would enable trait object upcasting? Like, adding a pointer to the supertrait's vtable in the subtrait's vtable. For now, Rust's dynamic dispatch doesn't seem to satisfy the Liskov substitution principle, which is a very basic principle for object-oriented design.
Of course you can use static dispatch, which is indeed very elegant to use in Rust, but it easily leads to code bloat which is sometimes more important than computational performance - like on embedded systems, and Rust developers claim to support such use cases of the language. Also, in many cases you can successfully use a model which is not purely Object-Oriented, which seems to be encouraged by Rust's functional design. Still, Rust supports many of the useful OO patterns... so why not the LSP?
Does anyone know the rationale for such design?
Actually, I think I got the reason. I found an elegant way to add upcasting support to any trait that desires it, and that way the programmer is able to choose whether to add that additional vtable entry to the trait, or prefer not to, which is a similar trade-off as in C++'s virtual vs. non-virtual methods: elegance and model correctness vs. performance.
The code can be implemented as follows:
trait Base: AsBase {
// ...
}
trait AsBase {
fn as_base(&self) -> &Base;
}
impl<T: Base> AsBase for T {
fn as_base(&self) -> &Base {
self
}
}
One may add additional methods for casting a &mut pointer or a Box (that adds a requirement that T must be a 'static type), but this is a general idea. This allows for safe and simple (although not implicit) upcasting of every derived type without boilerplate for every derived type.
As of Jun 2017, the status of this "sub-trait coercion" (or "super-trait coercion") is as follows:
An accepted RFC #0401 mentions this as a part of coercion. So this conversion should be done implicitly.
coerce_inner(T) = U where T is a sub-trait of U;
However, this is not yet implemented. There is a corresponding issue #18600.
There is also a duplicate issue #5665. Comments there explain what prevent this from being implemented.
Basically, the problem is how to derive vtables for super-traits. Current layout of vtables is as follows (in x86-64 case):
+-----+-------------------------------+
| 0- 7|pointer to "drop glue" function|
+-----+-------------------------------+
| 8-15|size of the data |
+-----+-------------------------------+
|16-23|alignment of the data |
+-----+-------------------------------+
|24- |methods of Self and supertraits|
+-----+-------------------------------+
It doesn't contain a vtable for a super-trait as a subsequence. We have at least to have some tweaks with vtables.
Of course there are ways to mitigate this problem, but many with differing advantages/disadvantages! One has a benefit for the vtable size when there is a diamond inheritance. Another is supposed to be faster.
There #typelist says they prepared a draft RFC which looks well-organized, but they look like disappeared after that (Nov 2016).
I ran into the same wall when I started with Rust.
Now, when I think about traits, I have a different image in mind than when I think about classes.
trait X: Y {} means when you implement trait X for struct S you also need to implement trait Y for S.
Of course this means that a &X knows it also is a &Y, and therefore offers the appropriate functions.
It would require some runtime-effort (more pointer dereferences) if you needed to traverse pointers to Y's vtable first.
Then again, the current design + additional pointers to other vtables probably wouldn't hurt much, and would allow easy casting to be implemented. So maybe we need both? This is something to be discussed on internals.rust-lang.org
This feature is so desired that there is both a tracking issue for adding it to the language, and an dedicated initiative repository for the people contributing to implementing it.
Tracking Issue: https://github.com/rust-lang/rust/issues/65991
Initiative Repository: https://github.com/rust-lang/dyn-upcasting-coercion-initiative
This is now working on stable rust, you can upcast to the base trait also you can call base trait functions directly from the derived trait object
trait Base {
fn a(&self) {
println!("a from base");
}
}
trait Derived: Base {
fn e(&self) {
println!("e from derived");
}
}
fn call_derived(d: &impl Derived) {
d.e();
d.a();
call_base(d);
}
fn call_base(b: &impl Base) {
b.a();
}
struct S;
impl Base for S {}
impl Derived for S {}
fn main() {
let s = S;
call_derived(&s);
}
playground link

Working around the limitations of extension traits

The pattern of having an object-safe trait Foo and a (potentially unsafe) extension trait FooExt implemented for all instances of Foo seems to become standard now.
https://github.com/rust-lang/rfcs/pull/445
This is a problem for me in the case of Iterator<A>, as I have a library that overrides the default method IteratorExt#last() of the old iterator trait (the underlying library has an efficient implementation of last()). This in now impossible, because for any A, there will always be a conflicting trait implementation of IteratorExt, the one that libcore already provides for all Iterator<A>.
iterator.rs:301:1: 306:2 error: conflicting implementations for trait `core::iter::IteratorExt` [E0119]
iterator.rs:301 impl<'a, K: Key> iter::IteratorExt<Vec<u8>> for ValueIterator<'a,K,Vec<u8>> {
iterator.rs:302 fn last(&mut self) -> Option<Vec<u8>> {
iterator.rs:303 self.seek_last();
iterator.rs:304 Some(self.value())
iterator.rs:305 }
iterator.rs:306 }
...
Now, as far as I see, I have two options:
have my own trait and my own last() implementation. That would mean it conflicts if IteratorExt is imported unless carefully used. This also has the danger accidentally using an inefficient version of last() if the version from IteratorExt is used. I'd loose convenient access to IteratorExt.
have my own trait and name the method differently (seek_last()). Disadvantage: I ask the user to learn vocabulary and to always favor my method over that provided by IteratorExt. Same problem: I'd like to avoid accidental usage of last().
Is there any other, better, solution I am missing?
As of rustc 0.13.0-nightly (8bca470c5 2014-12-08 00:12:30 +0000) defining last() as an inherent method on your type should work.
#[deriving(Copy)]
struct Foo<T> {t: T}
impl<T> Iterator<T> for Foo<T> {
fn next(&mut self) -> Option<T> { None }
}
// this does not work
// error: conflicting implementations for trait `core::iter::IteratorExt` [E0119]
// impl<T> IteratorExt<T> for Foo<T> {
// fn last(mut self) -> Option<T> { None }
//}
// but this currently does
impl<T> Foo<T> {
fn last(mut self) -> Option<T> { Some(self.t) }
}
fn main() {
let mut t = Foo{ t: 3u };
println!("{}", t.next())
println!("{}", t.last()) // last has been "shadowed" by our impl
println!("{}", t.nth(3)) // other IteratorExt methods are still available
}
Since you're not supposed to use Extension traits as generic bounds (but just to provide additional methods), this should theoretically work for your scenario, as you can have your own type and its impl in the same crate.
Users of your type will use the inherent last method instead of the one on IteratorExt but still be able to use the other methods on IteratorExt.
last should be moved to Iterator, rather than IteratorExt.
IteratorExt is needed when using Box<Iterator> objects, to allow calling generic methods on them (e.g. map), because you can't put a generic method in a vtable. However, last isn't generic, so it can be put in Iterator.