This question already has answers here:
How do I implement a trait I don't own for a type I don't own?
(3 answers)
Closed 7 years ago.
I want to provide an implementation of a trait ToHex (not defined by me, from serialize) for a primitive type u8:
impl ToHex for u8 {
fn to_hex(&self) -> String {
self.to_str_radix(16)
}
}
The problem is I get this compiler error:
error: cannot provide an extension implementation where both trait and type are not defined in this crate
I understand the reason of this error and its logic, this is because both the trait and the primitive type are external to my code. But how can I handle this situation and provide an ToHex implementation for u8? And more generally how do you handle this kind of issue, it seems to me that this problem must be common and it should be possible and easy to extend types like this?
You should use a newtype struct to do this:
pub struct U8(pub u8)
impl ToHex for U8 {
fn to_hex(&self) -> String {
let U8(x) = *self;
x.to_str_radix(16)
}
}
This does mean, however, that you should wrap u8 into U8 where you need to perform this conversion:
let x: u8 = 127u8
// println!("{}", x.to_hex()); // does not compile
println!("{}", U8(x).to_hex());
This is absolutely free in terms of performance.
I realize this is almost a year old, but the answer was never accepted and I think I've found an alternate solution, that I thought would be good to document here.
In order to extend the functionality of the u8 through traits, instead of trying to extend ToHex, why not create a new trait?
trait MyToHex {
fn to_hex(&self) -> String;
}
impl MyToHex for u8 {
fn to_hex(&self) -> String {
format!("{:x}", *self)
}
}
then used like so
fn main() {
println!("{}", (16).to_hex());
}
This has the advantage that you don't have to wrap every u8 variable with a new and superfluous data type.
The disadvantage is that you still can't use a u8 in a external function (i.e std library, or one you have no control over) that requires the ToHex trait (Vladimir Matveev's solution works in this case), but from OP it sounds like all you want to do is extend u8 only inside your code.
Related
This question already has an answer here:
Rust equivalent to Swift's extension methods to a protocol?
(1 answer)
Closed 8 months ago.
I want to create a new iterator method such as:
let test_data = vec![1,2,3,1,1,1,1];
let indexes_with_val_1 = test_data.iter().find_all(|element| element == 1).unwrap();
assert_eq!(indexes_with_val_1, vec!(0,3,4,5,6));
So essentially I want to add a new method to the std::iter::Iterator trait but can't find examples that work for this
The logic is not an issue as I have a free function which works fine, I just would like to be able to use it as I have in the code example for better ergonomics.
You can use a design pattern called extension traits. You can't extend the Iterator trait, but you can write a new one. Here's what we're going to do.
Write a new trait, IteratorExt, which has your custom method in it.
Write a blanket impl that implements IteratorExt for any type that implements Iterator.
Import IteratorExt to get access to your extension function.
For example, we can add a simple function called my_extension to iterators like so
trait IteratorExt {
fn my_extension(self) -> Self;
}
impl<T: Iterator> IteratorExt for T {
fn my_extension(self) -> Self {
println!("Hey, it worked!");
self
}
}
pub fn main() {
let x = vec!(1, 2, 3, 4);
let y = x.iter().my_extension().map(|x| x + 1).collect::<Vec<_>>();
println!("{:?}", y);
}
The only downside is that you have to import the new trait to use it. So if you want to use my_extension in another file, you have to import IteratorExt specifically in order to do so.
In my experience, the Rust community is somewhat divided on whether this is legitimate practice or whether it's a hack to be avoided, so your mileage may vary.
There exist the extension trait pattern for that.
The idea is that you create a trait, usually named TraitExt by convention, and implement it for all implementations of Trait:
pub trait IteratorExt {
fn my_iterator_extension(&self);
}
impl<I: Iterator + ?Sized> IteratorExt for I {
fn my_iterator_extension(&self) {
// Do work.
}
}
my_iterator.my_iterator_extension();
I'm working with webassembly so I need to fetch the pointer to a buffer. In the case where T is just AsRef<[f32]> (that is, it can be converted to a slice if I'm understanding correctly), I have solved it like this:
#[derive(TS, Serialize)]
pub struct PtrBufF32(usize);
impl<T> From<T> for PtrBufF32
where T: AsRef<[f32]>
{
fn from(f32arr: T) -> Self {
let slc: &[f32] = f32arr.as_ref();
let ptr: *const f32 = slc.as_ptr();
Self(ptr as usize)
}
}
I had help from another stack overflow user to understand what's going on -- as far as I'm understanding, this means "for any T that can be converted to a refence of &[f32] (that is, a slice), then we can implement this trait. The result is simply the pointer to the start of the slice of course.
But then in addition to implementing for anything that can be represented as &[f32], we need to implement for any collection of things that can be represented by &[f32]. Like, if my type T implements Into<&[f32]>, then I can implement the type for any AsRef<[T]>, right? And so on. Any collection of those also implements it. So I thought:
impl<T> From<T> for PtrBufF32
where T: AsRef<[dyn Into<PtrBufF32>]>
{
fn from(f32arr: T) -> Self {
todo!()
}
}
But no… apparently those are "conflicting implementations" somehow?
error[E0119]: conflicting implementations of trait `std::convert::From<memory::ptrbuf::PtrBufF32>` for type `memory::ptrbuf::PtrBufF32`
Yet, if I try to PtrBufF32::from(vec![T]) and T implements Into<AsRef<[f32]>>, it doesn't let me. So clearly it's not conflicting, is it?
Thanks
As long as you implemented conversion for undefined list of types you should think a little bit more widely.
Imagine some type ForeignType, that implements both Into<PtrBufF32> and AsRef<[f32]>. Then if you call PtrBufF32::from(my_foreign_type), compiler cannot decide which of two implementation he must use.
I want to convert a usize typed variable into a u32 typed variable in Rust. I am aware that the usize variable might contain a value larger than 2^32, and in that case the conversion should fail. I am trying to use the TryFrom trait to perform the conversion.
This is a simple example (Nightly Rust, Playground):
#![feature(try_from)]
use std::convert::TryFrom;
fn main() {
let a: usize = 0x100;
let res = u32::try_from(a);
println!("res = {:?}", res);
}
The code doesn't compile, with the following compilation error:
error[E0277]: the trait bound `u32: std::convert::From<usize>` is not satisfied
--> src/main.rs:6:15
|
6 | let res = u32::try_from(a);
| ^^^^^^^^^^^^^ the trait `std::convert::From<usize>` is not implemented for `u32`
|
= help: the following implementations were found:
<u32 as std::convert::From<std::net::Ipv4Addr>>
<u32 as std::convert::From<u8>>
<u32 as std::convert::From<char>>
<u32 as std::convert::From<u16>>
= note: required because of the requirements on the impl of `std::convert::TryFrom<usize>` for `u32`
I deduce from the compilation error that having TryFrom<usize> for u32 is dependent on having From<usize> for u32, which seems somewhat strange to me.
Is there any other way I could utilize TryFrom to convert from usize to u32? If not, is there any other idiomatic way to perform this conversion?
I know that I can use the as keyword, but it doesn't notify me if something went wrong with the conversion. In addition, I think that I can write my own function that does the conversion, but I would be surprised if Rust doesn't have some idiomatic way to do this conversion. usize and u32 are two basic types, after all.
Since this answer was created, it was decided to have the implementation of TryFrom<usize> always allow for the possibility of failure, regardless of the current platform. The original code now compiles successfully in Rust 1.34.
Original answer
having TryFrom<usize> for u32 is dependent on having From<usize> for u32, which seems somewhat strange to me
This is because there's a blanket implementation of TryFrom for anything that implements From:
impl<T, U> TryFrom<U> for T
where
T: From<U>,
{
type Error = !;
}
As you mentioned, since Rust supports platforms where the native integer length is 16, 32, or 64 bits, having such an implementation of From / Into would not be lossless on some of these platforms.
This error occurs because there's no direct implementation of TryFrom / TryInto for these types. This is because users of these traits prefer that the implementations be infallible when platform-appropriate (The type Error = !).
There is a separate tracking issue 49415 specifically for deciding this issue.
I think that I can write my own function that does the conversion
Yes, that is what you should do. Something like this untested piece of code:
use std::u32;
struct SomeError;
// usize is a u16 or u32, which always fits in a u32
#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
fn my_thing(a: usize) -> Result<u32, SomeError> {
Ok(a as u32)
}
// usize is a u64, which might be too big
#[cfg(target_pointer_width = "64")]
fn my_thing(a: usize) -> Result<u32, SomeError> {
if a > u32::MAX as usize {
Err(SomeError)
} else {
Ok(a as u32)
}
}
I would be surprised if Rust doesn't have some idiomatic way to do this conversion. usize and u32 are two basic types, after all.
The problem is that usize isn't really a "basic" type because it changes size depending on the target platform. Getting this correct, performant and ergonomic is not easy.
I'm new to Rust and have seen some examples of people using Box to allow pushing many types that implement a certain Trait onto a Vec. When using a Trait with Generics, I have run into an issue.
error[E0038]: the trait `collision::collision_detection::Collidable` cannot be made into an object
--> src/collision/collision_detection.rs:19:5
|
19 | collidables: Vec<Box<Collidable<P, M>>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `collision::collision_detection::Collidable` cannot be made into an object
|
= note: method `get_ncollide_shape` has generic type parameters
error: aborting due to previous error
error: Could not compile `game_proto`.
To learn more, run the command again with --verbose.
Here is my code
extern crate ncollide;
extern crate nalgebra as na;
use self::ncollide::shape::Shape;
use self::ncollide::math::Point;
use self::ncollide::math::Isometry;
use self::na::Isometry2;
pub trait Collidable<P: Point, M> {
fn get_ncollide_shape<T: Shape<P, M>>(&self) -> Box<T>;
fn get_isometry(&self) -> Isometry2<f64>;
}
pub struct CollisionRegistry<P, M>
where
P: Point,
M: Isometry<P>,
{
collidables: Vec<Box<Collidable<P, M>>>,
}
impl<P: Point, M: Isometry<P>> CollisionRegistry<P, M> {
pub fn new() -> Self {
let objs: Vec<Box<Collidable<P, M>>> = Vec::new();
CollisionRegistry { collidables: objs }
}
pub fn register<D>(&mut self, obj: Box<D>)
where
D: Collidable<P, M>,
{
self.collidables.push(obj);
}
}
I'm trying to use collidables as a list of heterogenous game objects that will give me ncollide compatible Shapes back to feed into the collision detection engine.
EDIT:
To clear up some confusion. I'm not trying to construct and return an instance of a Trait. I'm just trying to create a Vec that will allow any instance of the Collidable trait to be pushed onto it.
Rust is a compiled language, so when it compiles your code, it needs to know all of the information it might need to generate machine code.
When you say
trait MyTrait {
fn do_thing() -> Box<u32>;
}
struct Foo {
field: Box<MyTrait>
}
you are telling Rust that Foo will contain a box containing anything implementing MyTrait. By boxing the type, the compiler will erase any additional data about the data type that isn't covered by the trait. These trait objects are implemented as a set of data fields and a table of functions (called a vtable) that contains the functions exposed by the trait, so they can be called.
When you change
fn do_thing() -> Box<u32>;
to
fn do_thing<T>() -> Box<T>;
it may look similar, but the behavior is much different. Let's take a normal function example
fn do_thing<T>(val: T) { }
fn main() {
do_thing(true);
do_thing(45 as u32);
}
the compiler performs what is a called monomorphization, which means your code in the compiler becomes essentially
fn do_thing_bool(val: bool) { }
fn do_thing_num(val: u32) { }
fn main() {
do_thing_bool(true);
do_thing_num(45 as u32);
}
The key thing to realize is that you are asking it to do the same thing for your trait. The problem is that the compiler can't do it. The example above relies on knowing ahead of time that do_thing is called with a number in one case and a boolean in another, and it can know with 100% certainty that those are the only two ways the function is used.
With your code
trait MyTrait {
fn do_thing<T>() -> Box<T>;
}
the compiler does not know what types do_thing will be called with, so it has no way to generate functions you'd need to call. To do that, wherever you convert the struct implementing Collidable into a boxed object it would have to know every possible return type get_ncollide_shape could have, and that is not supported.
Other links for this:
Understanding Traits and Object Safety
https://www.reddit.com/r/rust/comments/3an132/how_to_wrap_a_trait_object_that_has_generic/
I have a struct that contains a reference and so it has a lifetime parameter. I'd like to pass around the function pointer of a method of this struct. Later, I will call that function with an instance of the struct. I ran into snags while trying to store the function pointer, eventually finding this solution:
struct Alpha<'a> { a: &'a u8 }
impl<'a> Alpha<'a> {
fn alpha(&self) -> u8 { *self.a }
}
struct Try1(fn(&Alpha) -> u8);
struct Try2(for<'z> fn(&Alpha<'z>) -> u8);
struct Try3<'z>(fn(&Alpha<'z>) -> u8);
fn main() {
Try1(Alpha::alpha); // Nope
Try2(Alpha::alpha); // Nope
Try3(Alpha::alpha);
}
Unfortunately, this solution doesn't work for my real case because I want to implement a trait that has its own notion of lifetimes:
trait Zippy {
fn greet<'a>(&self, &Alpha<'a>);
}
impl<'z> Zippy for Try3<'z> {
fn greet<'a>(&self, a: &Alpha<'a>) { println!("Hello, {}", self.0(a)) }
}
Produces the error:
error: mismatched types:
expected `&Alpha<'z>`,
found `&Alpha<'a>`
I feel that I shouldn't need to tie the lifetime of my struct Try3 to the lifetime of the parameter of the function pointer, but the compiler must be seeing something I'm not.
Unfortunately, the function alpha implemented on the struct Alpha effectively takes the struct's lifetime as a parameter, despite not actually using it. This is a limitation of the syntax for defining methods on structs with lifetimes. So even though it is possible to take a pointer to it as a for<'z> fn(&Alpha<'z>) -> u8, it is not possible to treat it as a fn(&Alpha) -> u8, even though the definition suggests this should be possible.
This can be worked around by defining a function that invokes the method and take a pointer to it instead:
fn workaround(a: &Alpha) -> u8 { Alpha::alpha(a) }
Try1(workaround);
In fact, it may be better to do it the other way around, with the definition in the function and the method invoking the function. Then when the function is invoked through a fn(&Alpha) -> u8 pointer a second jump won't be necessary into the method, and calls to the method can be inlined as calls to the function.