-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove From
implementations from the direction types
#10857
Conversation
My main concern with this is that it potentially makes a lot of APIs needlessly awkward. For example, creating a // Before
let segment = Segment2d::new(Vec2::X.into(), 2.0); // .into() could even be removed with impl Into<Direction2d>
// After
let segment = Segment2d::new(Direction2d::new(Vec2::X).unwrap(), 2.0); For rays, I was considering an API where I do see value in requiring the direction to always be valid, but NaN or non-finite values for directions should be rare enough that I'm hesitant to take the DX hit for it. I believe the main goal of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that the correctness is more important here. There's no guaranteed one-to-one mapping between a Vec2
and a direction unfortunately.
I'd be fine with a TryFrom
impl instead or in addition to this though.
@Jondolf, I definitely see your point, but IMO we should instead add explicitly infallible (panicking) constructors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After thinking about this more, I think I'm also leaning in favor of this change now. It does make some things a bit more verbose, but the correctness and explicitness is perhaps more important, and if desired, primitives could still have panicking constructors that handle the vector to direction conversion in the background.
I think having an additional TryFrom
impl would make sense because that's basically what new
does, but try_from
/try_into
might be less common and accessible for users so I'd still preserve new
.
…10884) # Objective Implement `TryFrom<Vec2>`/`TryFrom<Vec3>` for direction primitives as considered in #10857. ## Solution Implement `TryFrom` for the direction primitives. These are all equivalent: ```rust let dir2d = Direction2d::try_from(Vec2::new(0.5, 0.5)).unwrap(); let dir2d = Vec2::new(0.5, 0.5).try_into().unwrap(); // (assumes that the type is inferred) let dir2d = Direction2d::new(Vec2::new(0.5, 0.5)).unwrap(); ``` For error cases, an `Err(InvalidDirectionError)` is returned. It contains the type of failure: ```rust /// An error indicating that a direction is invalid. #[derive(Debug, PartialEq)] pub enum InvalidDirectionError { /// The length of the direction vector is zero or very close to zero. Zero, /// The length of the direction vector is `std::f32::INFINITY`. Infinite, /// The length of the direction vector is `NaN`. NaN, } ```
This removes the
From<Vec2/3>
implementations for the direction types.It doesn't seem right to have when it only works if the vector is nonzero and finite and produces NaN otherwise.
Added
Direction2d/3d::new
which usesVec2/3::try_normalize
to guarantee it returns either a valid direction orNone
.This should make it impossible to create an invalid direction, which I think was the intention with these types.