For example I have below generic struct:
use std::convert::From;
pub struct Rect<T> {
pub top_left: (T, T), // 0 is x, 1 is y.
pub bottom_right: (T, T), // 0 is x, 1 is y.
}
impl<T> Rect<T> {
pub fn new(top_left: (T, T), bottom_right: (T, T)) -> Self {
Rect { top_left, bottom_right }
}
pub fn height(&self) -> T {
self.bottom_right.1 - self.top_left.1
}
pub fn width(&self) -> T {
self.bottom_right.0 - self.top_left.0
}
}
pub struct Size<T> {
pub height: T,
pub width: T,
}
impl<T> Size<T> {
pub fn new(height: T, width: T) -> Self {
Size { height, width }
}
}
impl<T: Rect<H>> From<T> for Size<H> {
fn from(rect: T) -> Size<H> {
Size::new(rect.height() as T, rect.width() as T)
}
}
I had two generic types:
The Rect<T>
is a rectangle, it has top-left point and bottom-right point. The T
can be isize
, usize
, i32
, etc. The rectangle has a height
method and a width
method.
The Size<T>
is a size of a rectangle, it only as the height
and width
, no points. The T
can be isize
, usize
, i32
, etc.
Apparently, the Rect<T>
can be converted into a Size<T>
, so I want to implement the From
trait.
But I just don't know how to declare the generic parameter T
. Please help me.
impl<T: Rect<H>> From<T> for Size<H>
This doesn't work because you cannot bound on types, only on traits (and lifetimes). So T: Rect<H>
is invalid.
Instead, you want to say something like this:
impl<T, H> From<Rect<T>> for Size<H> {
fn from(rect: Rect<T>) -> Size<H> {
Size::new(rect.height(), rect.width())
}
}
This won't work either, though, because Size::<H>::new
expects you to give it H
's but you're giving it T
's. The simplest fix would be to say that you can convert a Rect<T>
to a Size<T>
(the types must match):
impl<T> From<Rect<T>> for Size<T> {
fn from(rect: Rect<T>) -> Size<T> {
Size::new(rect.height(), rect.width())
}
}
Alternatively, you can require that T: Into<H>
and then convert the height and width using .into()
:
impl<T: Into<H>, H> From<Rect<T>> for Size<H> {
fn from(rect: Rect<T>) -> Size<H> {
Size::new(rect.height().into(), rect.width().into())
}
}
Note also that your Rect::height
and Rect::width
methods don't compile because there's no guarantee that T
implements subtraction. You can fix this by breaking them out into their own impl
block which bounds T: std::ops::Sub<Output = T>
:
impl<T: std::ops::Sub<Output = T>> Rect<T> {
pub fn height(&self) -> T {
self.bottom_right.1 - self.top_left.1
}
pub fn width(&self) -> T {
self.bottom_right.0 - self.top_left.0
}
}
Now we have a new problem: the compiler seems to think we're trying to move the T
values in the Rect
, and indeed we are! The simple fix, if you are only going to use this with numeric primitives, would be to also require T: Copy
. This requires that T
be a bitwise-copyable value, and so instead of moving the T
s out of the fields of self
, they will be copied instead.
impl<T: std::ops::Sub<Output = T> + Copy> Rect<T> {
(You could instead bound on Clone
and then say self.bottom_right.1.clone() - self.top_left.1.clone()
, which would support types that can be cloned but not bitwise-copied, such as num_bigint::BigInt
.)
Okay, that's all working... but now the From<Rect<_>> for Size<_>
implementation broke. It's calling width
and height
on the Rect
value provided, but we can't because T
doesn't meet the bounds for them. All we have to do is copy the bounds we added above:
impl<T: Into<H> + std::ops::Sub<Output = T> + Copy, H> From<Rect<T>> for Size<H> {
And now everything works!