-
Notifications
You must be signed in to change notification settings - Fork 1.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
RFC: Generic conversion traits #529
Conversation
Just out of sheer curiosity, why the |
Writing two functions/methods with mostly-duplicated signatures is a significant ergonomics hit. I’m not convinced this is better than having lots of special-purpose conversion traits like |
Uh, nevermind. I just realized that having wrapper functions/methods to reduce the amount of monomorphization is orthogonal to whether conversion traits are generic or specific. |
LGTM, but the story in regard to ad-hoc conversion traits is still unclear to me. Would there generic impls like this? impl<T: Show> To<String> for T {
fn cnvt_to(&self) -> String { format!("{}", self) /* or however the concrete impl of todays ToString looks like */ }
}
trait ToString: To<String> {
fn to_string(&self) -> String { self.cnvt_to() }
}
impl<T: To<String>> ToString for T {} And if not, why? And how does this deal with conversions that do not have 1:1 mappings? (Like a |
I said this in IRC, and I'm writing it here for the record: Using the Using this sugar either for this reason and/or for abstract return types (#518) seems like a great idea to me. // instead of the previously proposed -> impl Iterator<i32> syntax
fn func() -> ~Iterator<i32> { ... } |
Please don't add more unnecessary abbreviations in APIs! Rust APIs already have a ton of these unfriendly, confusing and unreadable names; let's not add more. |
I might be missing something but I don't see how the four proposed conversion traits could replace impl FromError<io::IoError> for MyCoolError { ... } would be replaced, using the impl Into<MyCoolError> for io::IoError { ... } which, unless I misunderstand coherence, could not be written anywhere but the crate where I also agree that the meaning of |
Is there any ways to stabilize the If not, I prefer the more radical alternative. @tomjakubowski Interesting point! Perhaps the coherence rules are / should be: as long as at least |
I agree that the |
@rkjnsn: |
it seems to me that what we really need is a way to tap into the coercion system. basically allow defined coercions to be used with the as syntax. I have no idea how that would manifest but it would clean up the syntax at conversion at least. |
Hi all, sorry for the very long absence on this thread. (It was put on the backburner for the initial alpha cycle.) Now that the dust is settling a bit, and we've been continuing to add new ad hoc conversion traits that could as easily use these generic ones, I want to revive this RFC. Assuming we address the naming (the Detailed responses are below.
Mostly because I hoped it would spur conversation to find a better name :-) Worst case, we go with
Yes.
This is similar to the problem of implementing
You are correct: given our coherence rules, In general, I think that there will still be a role for traits like
We may want to do this in the long run, yes. Some ideas along these lines are sketched briefly in the RFC, but I think we should start by simply having the traits at all. |
I think it's good to go in my opinion, but yeah let's not keep Hey @alexcrichton, perhaps a few default implementations of this for FFI types satisfy some of your ideas in #892? I think your: impl Convert<libc::c_int> for bool {
fn convert(&self) -> libc::c_int { *self as libc::c_int }
} I think would be something like: impl Into<libc::c_int> for bool {
fn cvt_into(self) -> libc::c_int { *self as libc::c_int }
} This one: impl<'a, T> Convert<*const T> for &'a T {
fn convert(&self) -> *const T { *self as *const T }
} would probably be: impl<'a, T> To<*const T> for &'a T {
fn cvt_to(&self) -> *const T { *self as *const T }
} Note I typed them without checking, so I may be off, but it seems like it can be done with this, though the difference between this and yours is that now you can't use the same method for each, I guess. I think that was part of the benefit? So that you could blindly call it on anything within a macro? I'm reminded of @aturon's conversation about a trait that generalizes over any bound, perhaps that could be created for these to be able to use a single method? Not sure. |
@blaenk that's a good point! I'd have to think about whether these general purpose traits should be used for that specific of a context, but my first impression would be that they would fit nicely. It's true though that a macro-based approach would require everything implement precisely one of the traits (as opposed to a few different ones), but I think it's ok. |
``` | ||
|
||
What's worse, if you need a conversion that works over any lifetime, | ||
*there's no way to specify it*: you can't write something like |
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.
This almost works now:
trait Foo<T>: Sized { fn bar(self, other: T) {} }
fn foo<T>() where for<'a> &'a T: Foo<&'a T> {}
fn main() {}
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 also wonder if we could do lifetime elision for where clauses like this, meaning &T: As<&MyType>
would just work.)
I have an old branch where I added the [1] IIRC, the problem was that |
Another vote for |
I've pushed an update with |
} | ||
``` | ||
|
||
The interaction between |
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 is this?
I'm getting a bit cagey about are coercion and equivalence story here; we have Deref, Borrow, and As, To, Into, and From now? |
Note: some ongoing discussion at a WIP PR. |
Another possible choice of method names: |
This RFC has fairly broad support in the overall vision, and there are only some lingering concerns in the last few details of this approach. The PR-in-progress is already quite promising and only has a few minor tweaks. As a result I'm going to merge this. Tracking issue: rust-lang/rust#23567 |
RFC 529[1] has landed. [1] rust-lang/rfcs#529
Note that the implementation has diverged somewhat from this RFC, based on experiences rolling out. In particular, we moved to Meanwhile, @reem has pointed out that there's a much cleaner version of the alternative design that allows you to use methods like trait AsRef<T: ?Sized> {
fn convert_as_ref(&self) -> &T;
}
trait Into<T> {
fn convert_into(self) -> T;
}
trait From<T> {
fn from(T) -> Self;
}
trait Convert {
fn as_ref<T>(&self) -> &T where Self: AsRef<T>;
fn into<T>(self) -> T where Self: Into<T>;
}
impl<T> Convert for T {
fn as_ref<T>(&self) -> &T where Self: AsRef<T> {
self.convert_as_ref()
}
fn into<T>(self) -> T where Self: Into<T> {
self.convert_into()
}
}
// Now you can do:
foo.into::<Bar>()
baz.as_ref::<str>() This is not very much more complex than the current traits (though you do need to understand the double dispatch to make sense of it), but re-opens the door to the generic traits replacing many ad hoc conversion methods. In particular, it may still be possible to avoid the need for Thoughts? |
Note, my main concern about this last proposal is that it introduces an additional barrier to understanding for newcomers, and unless we can remove
|
Just so we don't forget about the possibility: we could probably create a reflexive |
I think we make far too big a deal about the |
I like it. I don't agree with @reem that |
I agree with @reem that |
Wanted to give an update on this: After much discussion, we've decided to stick with the design that was landed, modulo some tweaks to the blanket implementations. As outlined above, we intend to retain convenience conversion methods, so e.g. there will be a Consequently, there is not much benefit to allowing the explicit type instantiation we've been discussing lately in this thread. Since the generic conversion traits are meant for, well, generic contexts, an annotation should almost never be necessary. If it is desired for clarity, type ascription should provide an easy means of providing. OTOH, providing type instantiation would involve substantial extra complexity for the traits (meaning that the methods go through double dispatch), which would further increase the barrier to newcomers. For such a basic concept as conversions, this would be a real problem. Note that we can always provide methods with explicit type instantiation later, just with different names than the ones used here. In the stabilization PR, the blanket impls are tweaked a bit so that both |
Is |
So, for example, taking |
@aturon YESSS!!! Very late response to your cc of me from 9 days ago in On Thursday, April 9, 2015, Aaron Turon notifications@github.com wrote:
|
@aturon That makes sense. Should the RFC be updated to reflect the implementation? |
This RFC proposes several new generic conversion traits. The
motivation is to remove the need for ad hoc conversion traits (like
FromStr
,AsSlice
,ToSocketAddr
,FromError
) whose sole roleis for generics bounds. Aside from cutting down on trait
proliferation, centralizing these traits also helps the ecosystem
avoid incompatible ad hoc conversion traits defined downstream from
the types they convert to or from. It also future-proofs against
eventual language features for ergonomic conversion-based overloading.
Rendered