-
Notifications
You must be signed in to change notification settings - Fork 202
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
Implement a MapCoordsFallible trait #191
Conversation
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.
What's the point of using a new dependency (failure
)? I've seen that crate around in Rust news, but I've never used it nor looked into it. Rust has an Err
option, which is what I'd use if I was implementing this.
If there's a 100 point LineString
, and you call map_coords_inplace_failible
, which is OK for first 10 points, and then fails, won't this make the LineString wrong because only the first 10 will have changed? The only way to work around this is to create a new LineString and std::mem::replace
it in, right?
I should probably write a test that simulates this, but my intent is that each map call |
|
Sure, but since you're modifying the object in place, won't the modifications to the first 10 points still be there? Those can't be undone. You could change the docs to say "Check the returned error of this, and if it's an error the object is now in an inconsistant state and shouldn't be used" |
Ah, I see what you mean. I had assumed that an |
Hmm the more I think about the fallible in-place version, the less I like what I've done. @rory is correct here I think – mem::replace is a better idea. If any of the operations do fail, it seems much better to return the unchanged version as an |
This trait is identical to MapCoords, but is intended for use when mapping fallible functions over geometries
Also add note on the consistency of geometries upon which failed operations have been carried out.
@@ -59,16 +109,37 @@ impl<T: CoordinateType, NT: CoordinateType> MapCoords<T, NT> for Point<T> { | |||
} | |||
} | |||
|
|||
impl<T: CoordinateType> MapCoordsInplace<T> for Point<T> { | |||
impl<T: CoordinateType, NT: CoordinateType> MapCoordsFallible<T, NT> for Point<T> { |
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.
instead of having all these explicit MapCoordsFallible
impl
s, what if we had a blanket impl
like this (i haven't tried compiling it so it might be wrong):
impl<T: CoordinateType, NT: CoordinateType, G: MapCoords<T, NT>> MapCoordsFallible<T, NT> for G {
type Output = G::Output;
fn map_coords_fallible(
&self,
func: &Fn(&(T, T)) -> Result<(NT, NT), Error>,
) -> Result<Self::Output, Error> {
Ok(self.map_coords(func))
}
}
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 don't think we can implement it in terms of map_coords
, because of the different input function signatures:
map_coords
is func: &Fn(&(T, T)) -> (NT, NT)
map_coords_fallible
is func: &Fn(&(T, T)) -> Result<(NT, NT), Error>
And (I think) because we're also missing a try!
or ?
to check for an early return, somewhere.
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.
yep, you're right 👍
i don't think there's a way to make this work since we can't return from the outer function within the closure if func
returs an error
I haven't throught about this, but you could probably replace all the MapCoords with calls to MapCoordsInplaceFalliable and appropriate clones and wrapping functions.
MapcoordsFaillible is just a clone & MapCoordsInplaceFalliable. Mapcoordsinplace is just a wrapping the function to always return Ok() and then call MapCoordsInplaceFalliable etc etc.
…On 18 March 2018 15:55:21 CET, "Stephan Hügel" ***@***.***> wrote:
urschrei commented on this pull request.
> @@ -59,16 +109,37 @@ impl<T: CoordinateType, NT: CoordinateType>
MapCoords<T, NT> for Point<T> {
}
}
-impl<T: CoordinateType> MapCoordsInplace<T> for Point<T> {
+impl<T: CoordinateType, NT: CoordinateType> MapCoordsFallible<T, NT>
for Point<T> {
I don't *think* we can implement it in terms of `map_coords`, because
of the different input function signatures:
`map_coords` is `func: &Fn(&(T, T)) -> (NT, NT)`
`map_coords_fallible` is `func: &Fn(&(T, T)) -> Result<(NT, NT),
Error>`
And (I think) because we're also missing a `try!` or `?` to check for
an early return, somewhere.
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.
|
Can you sketch this out (or send a PR to my branch)? As for |
TBH, I'm not convinced that in place failible is a good idea.
Leaving an object in a strange/invalid state if one transform fails sounds like a really bad idea, because programmers won't read the docs and it will cause a bug. 🙂
I made the MapCoordsinplace because I didn't want clones, and inplacefailible having "hidden" clones seems bad. So I don't know the solution.
We could replace mapcoords with mapcoordsinplace though. I'll do a patch later
…On 18 March 2018 16:44:48 CET, "Stephan Hügel" ***@***.***> wrote:
> MapcoordsFaillible is just a clone & MapCoordsInplaceFalliable.
Mapcoordsinplace is just a wrapping the function to always return Ok()
and then call MapCoordsInplaceFalliable etc etc.
Can you sketch this out (or send a PR to my branch)?
As for `MapCoordsinplaceFallible`, I've been tying myself in knots
trying to get it right, and I don't know whether it's worth it – the
best case scenario is that it's hiding a clone operation which means
that someone (probably one of us) will end up complaining about memory
usage and/or slowness at some point.
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.
|
I agree, I'll get rid of it. |
More trouble than it's worth
looks good to me. a little skeptical about incorporating bors r+ |
👎 Rejected by code reviews |
i guess i should wait for @rory to dismiss their review (or approve the pr). any other concerns? |
bors r+ |
Build succeeded |
192: Implement rust-proj as an optional feature within geo r=frewsxcv a=urschrei Continuing the discussion from #190 and #32. ## Introduction `rust-proj` is currently (as of `0.3.0`) using PROJ v4.9.x, and depends upon `Geo` for its `Point` type. However, we now have a `proj-sys` crate, with low-level bindings to PROJ v5.0.0. Unfortunately, `rust-proj's` dependency on `Geo` means that we can't directly use its functions within `Geo`, as this introduces a circular dependency (see #190). ### This PR: - Re-implements `rust-proj`'s functionality as an optional feature ("`proj`") within `Geo` using `proj-sys` - it has to be optional, as it requires PROJ v5.0.0 to be present - Makes two new traits –`Project` and `Convert` – available to any geometries implementing the `MapCoordsFallible` trait (see #191), allowing them to be (inverse) projected to and from Geodetic coordinates, and converted between projections. This functionality is a slightly more granular version (in keeping with the new PROJ API, which makes the same distinction) of what's possible using `pyproj` and `Shapely` geometries (see [here](http://shapely.readthedocs.io/en/stable/manual.html#other-transformations) for the official example): ```python from shapely.geometry import Point, LineString import pyproj # LineString in projected coordinates (OSGB36) ls = LineString([(548295.39, 182498.46), (548296.39, 182499.46)]) osgb36=pyproj.Proj("+init=EPSG:27700") eastings, northings = list(zip(*ls.coords)) # inverse-project to geodetic lon, lat coordinates (WGS 84) conv_ls = zip(*osgb36(eastings, northings, inverse=True)) # compare to command-line proj output assert conv_ls == [(0.13774816464635564, 51.52142699952049), (0.1377629902608001, 51.5214357231341)] ``` I propose that we leave `rust-proj` at 0.3.0, clearly marking it as deprecated, directing users to install `Geo` with the `proj` feature if they want v5.0.x features, and linking to `proj-sys` for implementers who don't wish to use `Geo`, for whatever reason. We can also (as per my and @turboladen's discussion in #32) highlight the availability of this feature more clearly, if need be. Co-authored-by: Stephan Hügel <urschrei@gmail.com>
This trait is identical to MapCoords, but is intended for use when mapping fallible functions over geometries.
It also adds Failure as a dependency, but I think that's OK.