I am looking to implement 2D matrix functionality in Rust in a generic fashion where the elements of the matrix would be numerics (either i32, i64, u32, u64, f32, f64). The generic type would look something like shown below:
#[derive(Debug)]
pub struct Mat<T> {
data: Vec<T>,
shape: (usize, usize),
}
impl<T> Mat<T> where T: i32 OR i64 OR u32 OR u64 OR f32 OR f64{
pub fn new(){
...
}
}
I know that you can AND trait bounds with the + symbol in the form of "where T: bound1 + bound2". Is there a way to cleanly OR trait bounds together?
No, but this isn't usually what you want anyway. You can do this by declaring a tag trait, like:
pub trait NumberType {}
impl NumberTrait for i32 {}
impl NumberTrait for i64 {}
// and so on...
impl<T> Mat<T> where T: NumberType { ... }
However, usually what you're actually trying to accomplish is enforcing that T supports some set of operations like addition and multiplication. If so, just bound on those operations:
use std::ops::*;
impl<T> Mat<T> where T: Add<T, Output=T> + Mul<T, Output=T> { ... }
This will cover all of the primitive numeric types, but will also cover other types for which those operations are defined, such as third-party "decimal" or "big number" (e.g. 128/256/512-bit integers) types.
Related
I have a container type like this:
struct Container<T: Sized + MyTrait + Serialize> {
data: T,
key: String
}
wrapping this trait:
trait MyTrait {
fn do_something(&self) -> Something;
}
I have many concrete implementors of MyTrait:
struct S1 {}
impl MyTrait for S1 { ... };
... many kinds ...
struct S10{}
impl MyTrait for S10 { ... }
I have a higher level which should vend the appropriate implementation based on some decision logic (a factory of MyTrait types):
fn get_appropriate_impl(type_of_instance: SomeEnum ... ) -> Container<MyTrait> {
...choose appropriate implementation to return...
return match type_of_instance {
// each branch should return a different type
}
}
I get an error
Container<MyTrait> {
doesn't have a size known at compile-time
I've tried many variations:
Container<Box<MyTrait>>
Box<Container<MyTrait>>
Box<Container<Box<MyTrait>>
each has their own errors
How do I resolve this?
PS - I've also implemented Deref and DerefMut on Container:
impl<T: MyTrait > Deref for Container<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
You are trying to return a trait, not an object. The size of an implementation of a trait is not known at compile time (imagine, you implement MyTrait for u8 and u16, what size the return type would have?). The Sized bound does not help here (and it's implicit, too), because it only requires that T is Sized, but not MyTrait. Also, adding a bound to MyTrait: Sized does not help because it only requires, that the object, you implement MyTrait for, is sized, but it does not specify which size it may have.
This can be solved by either returning a concrete type, which is implementing MyTrait or wrapping the trait (e.g. in a Box). You are probably looking for the latter approach.
Returning a concrete type can be either done by adding a generic argument to the function:
fn get_appropriate_impl<T: MyTrait>() -> Container<T> {
...
}
or by using the impl Trait notation:
fn get_appropriate_impl() -> Container<impl MyTrait> {
...
}
However, if you don't know the return type beforehand, you probably want to store the object on the heap and return a pointer to that, e.g. by using Box, Rc, or Arc:
fn get_appropriate_impl() -> Container<Box<dyn MyTrait>> {
...
}
This requires you to implement MyTrait on Box<dyn MyTrait> as well, this can be done by implementing it on Box yourself:
impl MyTrait for Box<dyn MyTrait> {
fn do_something(&self) -> Something {
(**self).do_something()
}
}
If you know that you will only use Box for Container<T> you might want to encapsulate it there directly:
struct Container {
data: Box<dyn MyTrait>,
}
When extending traits defined in other crates, there seem to be two ways to default implement a new trait.
The original definition of a trait is
pub trait Trait1 {
fn f1(&self);
}
In order to extend the functionality of this trait, we define a trait Trait2,
pub trait Trait2 {
fn f2(&self);
}
Now, because we want the functionality to be available by default, we can implement the following
impl<T> Trait2 for T
where
T: Trait1,
{
pub fn f2(&self) {
self.f1()
}
}
impl Trait2 for dyn Trait1 {
pub fn f2(&self) {
self.f1()
}
}
What I have observed is that, when mixing with trait objects, both of these implementations are required.
I understand that the impl<T> one is for concrete classes where as other is for dyn objects. Is that correct? Is there any way to share the default implementation here for both of these types? In my scenario, I had to copy and paste the whole implementation with just the change of the first line.
If I understood your question correctly, just add the unsized (?Sized) bound to T:
impl<T> Trait2 for T where T: Trait1 + ?Sized
This implements Trait2 for unsized types (eg. dyn Trait1) that implement Trait1 as well. By default, all type parameters are sized, and hence do not match unsized types.
Playground
This piece of code calls val.into() function where val is a serde_json::Value enum, but I can't find any description of into function in the Rust docs.
pub fn parse(&self, s: &str) -> Result<RpcObject, ReadError> {
let val = serde_json::from_str::<Value>(&s)?;
if !val.is_object() {
Err(ReadError::NotObject)
} else {
Ok(val.into())
}
}
Into is a trait with the single method into. Into is implemented for every type that implements From:
impl<T, U> Into<U> for T
where
U: From<T>,
serde_json::Value implements many different versions of From
impl From<i8> for Value
impl From<i16> for Value
impl From<i32> for Value
impl From<i64> for Value
impl From<isize> for Value
impl From<u8> for Value
impl From<u16> for Value
impl From<u32> for Value
impl From<u64> for Value
impl From<usize> for Value
impl From<f32> for Value
impl From<f64> for Value
impl From<bool> for Value
impl From<String> for Value
impl<'a> From<&'a str> for Value
impl<'a> From<Cow<'a, str>> for Value
impl From<Map<String, Value>> for Value
impl<T: Into<Value>> From<Vec<T>> for Value
impl<'a, T: Clone + Into<Value>> From<&'a [T]> for Value
These two traits are used to provide conversions between types that cannot fail. The traits TryFrom and TryInto allow for fallible conversions starting in Rust 1.34.
See also:
When should I implement std::convert::From vs std::convert::Into?
From and Into in Rust by Example
An astute reader will have noticed that what I showed above actually allows you to convert to a serde_json::Value. Technically, the original code converts to a RpcObject from a Value. Somewhere in your code there is impl From<serde_json::Value> for RpcObject, but since that implementation isn't provided, I can't link to any useful documentation for that, but the pattern is the same.
I am trying to extend the functionality of the Iterator trait.
My statistics/iter_statistics.rs:
mod iter_statistics {
pub trait IterStatistics: Iterator<Item = f64> {
fn foo(&mut self) -> f64 {
0.0
}
}
impl IterStatistics for Iterator<Item = f64> {}
}
And statistics/mod.rs:
pub use self::iter_statistics::*;
mod iter_statistics;
And finally in my test code I have
use statistics::IterStatistics;
fn main() {
let z: Vec<f64> = vec![0.0, 3.0, -2.0];
assert_eq!(z.into_iter().foo(), 0.0);
}
When I run the test, I get:
error: no method name `foo` found for type `std::vec::IntoIter<f64>` in the current scope
assert_eq!(z.into_iter().foo(), 0.0);
^~~
which is strange to me since the docs for IntoIter<T> say it implements Iterator<Item=T>.
The impl you have written will only apply to trait objects (e.g. &mut Iterator<Item=f64>), not for all types that implement Iterator<Item=f64>. You want to write a generic impl like this:
impl<T: Iterator<Item=f64>> IterStatistics for T {}
Your implementation is backward. When programming in Rust, you have to forget about OO-inheritance and reason in terms of capabilities.
What does trait D: B means in Rust?
This means that D can only be implemented for types that already implement B. It's not inheritance, it's a constraint.
When to use trait D: B then?
The main reason to use this constraint is when you wish to provide a default implementation of D methods that will require associated items (traits, constants, methods) from B.
In general, you do not want to add more constraints than strictly necessary, as your clients may wish to use this trait in ways you did not foresee.
The one exception is when creating a trait as a "bundle of constraints", so that you do not have type T: SomeTrait + SomeOtherTrait + Send for all the methods your are implementing. This "bundle of constraints" should be empty, then; it's not a functional trait after all, just an "alias".
So, how to extend Iterator?
First declare a new trait:
pub trait IterStatistics {
fn foo(&mut self) -> f64;
}
Then implement it for all types already implementing Iterator<Item = f64>:
impl<T> IterStatistics for T
where T: Iterator<Item = f64>
{
fn foo(&mut self) -> f64 {
0.0
}
}
In theory, Dynamically-Sized Types (DST) have landed and we should now be able to use dynamically sized type instances. Practically speaking, I can neither make it work, nor understand the tests around it.
Everything seems to revolve around the Sized? keyword... but how exactly do you use it?
I can put some types together:
// Note that this code example predates Rust 1.0
// and is no longer syntactically valid
trait Foo for Sized? {
fn foo(&self) -> u32;
}
struct Bar;
struct Bar2;
impl Foo for Bar { fn foo(&self) -> u32 { return 9u32; }}
impl Foo for Bar2 { fn foo(&self) -> u32 { return 10u32; }}
struct HasFoo<Sized? X> {
pub f:X
}
...but how do I create an instance of HasFoo, which is DST, to have either a Bar or Bar2?
Attempting to do so always seems to result in:
<anon>:28:17: 30:4 error: trying to initialise a dynamically sized struct
<anon>:28 let has_foo = &HasFoo {
I understand broadly speaking that you can't have a bare dynamically sized type; you can only interface with one through a pointer, but I can't figure out how to do that.
Disclaimer: these are just the results of a few experiments I did, combined with reading Niko Matsakis's blog.
DSTs are types where the size is not necessarily known at compile time.
Before DSTs
A slice like [i32] or a bare trait like IntoIterator were not valid object types because they do not have a known size.
A struct could look like this:
// [i32; 2] is a fixed-sized vector with 2 i32 elements
struct Foo {
f: [i32; 2],
}
or like this:
// & is basically a pointer.
// The compiler always knows the size of a
// pointer on a specific architecture, so whatever
// size the [i32] has, its address (the pointer) is
// a statically-sized type too
struct Foo2<'a> {
f: &'a [i32],
}
but not like this:
// f is (statically) unsized, so Foo is unsized too
struct Foo {
f: [i32],
}
This was true for enums and tuples too.
With DSTs
You can declare a struct (or enum or tuple) like Foo above, containing an unsized type. A type containing an unsized type will be unsized too.
While defining Foo was easy, creating an instance of Foo is still hard and subject to change. Since you can't technically create an unsized type by definition, you have to create a sized counterpart of Foo. For example, Foo { f: [1, 2, 3] }, a Foo<[i32; 3]>, which has a statically known size and code some plumbing to let the compiler know how it can coerce this into its statically unsized counterpart Foo<[i32]>. The way to do this in safe and stable Rust is still being worked on as of Rust 1.5 (here is the RFC for DST coercions for more info).
Luckily, defining a new DST is not something you will be likely to do, unless you are creating a new type of smart pointer (like Rc), which should be a rare enough occurrence.
Imagine Rc is defined like our Foo above. Since it has all the plumbing to do the coercion from sized to unsized, it can be used to do this:
use std::rc::Rc;
trait Foo {
fn foo(&self) {
println!("foo")
}
}
struct Bar;
impl Foo for Bar {}
fn main() {
let data: Rc<Foo> = Rc::new(Bar);
// we're creating a statically typed version of Bar
// and coercing it (the :Rc<Foo> on the left-end side)
// to as unsized bare trait counterpart.
// Rc<Foo> is a trait object, so it has no statically
// known size
data.foo();
}
playground example
?Sized bound
Since you're unlikely to create a new DST, what are DSTs useful for in your everyday Rust coding? Most frequently, they let you write generic code that works both on sized types and on their existing unsized counterparts. Most often these will be Vec/[] slices or String/str.
The way you express this is through the ?Sized "bound". ?Sized is in some ways the opposite of a bound; it actually says that T can be either sized or unsized, so it widens the possible types we can use, instead of restricting them the way bounds typically do.
Contrived example time! Let's say that we have a FooSized struct that just wraps a reference and a simple Print trait that we want to implement for it.
struct FooSized<'a, T>(&'a T)
where
T: 'a;
trait Print {
fn print(&self);
}
We want to define a blanket impl for all the wrapped T's that implement Display.
impl<'a, T> Print for FooSized<'a, T>
where
T: 'a + fmt::Display,
{
fn print(&self) {
println!("{}", self.0)
}
}
Let's try to make it work:
// Does not compile. "hello" is a &'static str, so self print is str
// (which is not sized)
let h_s = FooSized("hello");
h_s.print();
// to make it work we need a &&str or a &String
let s = "hello"; // &'static str
let h_s = &s; // & &str
h_s.print(); // now self is a &str
Eh... this is awkward... Luckily we have a way to generalize the struct to work directly with str (and unsized types in general): ?Sized
//same as before, only added the ?Sized bound
struct Foo<'a, T: ?Sized>(&'a T)
where
T: 'a;
impl<'a, T: ?Sized> Print for Foo<'a, T>
where
T: 'a + fmt::Display,
{
fn print(&self) {
println!("{}", self.0)
}
}
now this works:
let h = Foo("hello");
h.print();
playground
For a less contrived (but simple) actual example, you can look at the Borrow trait in the standard library.
Back to your question
trait Foo for ?Sized {
fn foo(&self) -> i32;
}
the for ?Sized syntax is now obsolete. It used to refer to the type of Self, declaring that `Foo can be implemented by an unsized type, but this is now the default. Any trait can now be implemented for an unsized type, i.e. you can now have:
trait Foo {
fn foo(&self) -> i32;
}
//[i32] is unsized, but the compiler does not complain for this impl
impl Foo for [i32] {
fn foo(&self) -> i32 {
5
}
}
If you don't want your trait to be implementable for unsized types, you can use the Sized bound:
// now the impl Foo for [i32] is illegal
trait Foo: Sized {
fn foo(&self) -> i32;
}
To amend the example that Paolo Falabella has given, here is a different way of looking at it with the use of a property.
struct Foo<'a, T>
where
T: 'a + ?Sized,
{
printable_object: &'a T,
}
impl<'a, T> Print for Foo<'a, T>
where
T: 'a + ?Sized + fmt::Display,
{
fn print(&self) {
println!("{}", self.printable_object);
}
}
fn main() {
let h = Foo {
printable_object: "hello",
};
h.print();
}
At the moment, to create a HasFoo storing a type-erased Foo you need to first create one with a fixed concrete type and then coerce a pointer to it to the DST form, that is
let has_too: &HasFoo<Foo> = &HasFoo { f: Bar };
Calling has_foo.f.foo() then does what you expect.
In future these DST casts will almost certainly be possible with as, but for the moment coercion via an explicit type hint is required.
Here is a complete example based on huon's answer. The important trick is to make the type that you want to contain the DST a generic type where the generic need not be sized (via ?Sized). You can then construct a concrete value using Bar1 or Bar2 and then immediately convert it.
struct HasFoo<F: ?Sized = dyn Foo>(F);
impl HasFoo<dyn Foo> {
fn use_it(&self) {
println!("{}", self.0.foo())
}
}
fn main() {
// Could likewise use `&HasFoo` or `Rc<HasFoo>`, etc.
let ex1: Box<HasFoo> = Box::new(HasFoo(Bar1));
let ex2: Box<HasFoo> = Box::new(HasFoo(Bar2));
ex1.use_it();
ex2.use_it();
}
trait Foo {
fn foo(&self) -> u32;
}
struct Bar1;
impl Foo for Bar1 {
fn foo(&self) -> u32 {
9
}
}
struct Bar2;
impl Foo for Bar2 {
fn foo(&self) -> u32 {
10
}
}