Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upMake function pointer types look like borrowed pointer types for forwards compatability #883
Conversation
Ericson2314
added some commits
Feb 17, 2015
glaebhoerl
reviewed
Feb 18, 2015
| # Summary | ||
|
|
||
| It is likely that Rust will want to someday have a notion of an unboxed function type to be used | ||
| with custom pointer types[1]. Ideally then today's function pointers would become `&'static`s of |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Ericson2314
Feb 19, 2015
Author
Contributor
In this case I wanted an actual footnote. But maybe there is a way I can make that clearer / look better.
This comment has been minimized.
This comment has been minimized.
glaebhoerl
Feb 19, 2015
Contributor
Oh right. There's usually a way to do superscripts but I think it's nonstandard, foo^1 on reddit and foo<sup>1</sup> on GH. (let'ssee... yup.)
This comment has been minimized.
This comment has been minimized.
Ericson2314
Feb 19, 2015
Author
Contributor
Done! How awful is it that allow tags in their markdown....
glaebhoerl
reviewed
Feb 18, 2015
| pointers. | ||
|
|
||
| Adding unboxed function types begs the questions of what would happen to the current function | ||
| pointers where that route taken. Since they act just like immutable borrowed static pointers to |
This comment has been minimized.
This comment has been minimized.
glaebhoerl
reviewed
Feb 18, 2015
| Adding unboxed function types begs the questions of what would happen to the current function | ||
| pointers where that route taken. Since they act just like immutable borrowed static pointers to | ||
| functions, it would be nice if they became that in actuality, lest two ways to do the same thing be | ||
| introduced so deeply in the language. Unfortunately, combining the two requires a braking syntactic |
This comment has been minimized.
This comment has been minimized.
glaebhoerl
reviewed
Feb 18, 2015
| function pointers fat, and moreover the sizes of functions are not in-general known dynamically | ||
| anyway (c.f. foreign functions). | ||
|
|
||
| So if this requires a breaking change to make nice, yet is to big to do before 1.0, what can be |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
scialex
commented
Feb 18, 2015
|
What is the benefit of this over simply using the |
This comment has been minimized.
This comment has been minimized.
|
@scialex the The best hack in the existing language is to create a new type of a function pointer with a phantom lifetime for borrowed pointers, and a newtype with a drop instance for owned pointers. |
Ericson2314
added some commits
Feb 19, 2015
This comment has been minimized.
This comment has been minimized.
bombless
commented
Feb 19, 2015
|
Then how do we handle extern-functions? And can we remove that optional argument name? #838 I'll try making a PoC soon. |
This comment has been minimized.
This comment has been minimized.
|
I like this RFC, but I think it should be adjusted to require |
This comment has been minimized.
This comment has been minimized.
|
+1 to the RFC with @P1start's amendment
Unfortunately, IIRC, a similar idea was already discussed and rejected. |
This comment has been minimized.
This comment has been minimized.
|
+1 to this with amendment. @P1start, do you mean @petrochenkov, was that discussion before the implementation of DST? |
This comment has been minimized.
This comment has been minimized.
Florob
commented
Feb 20, 2015
|
Particularly with the amendment, I really like this. It could alleviate the fallout from the recent-ish change that each function has its own type. I.e. where we now need: fn f(x: Ty) { ... }
let f: fn(Ty) = f; // coerce to fn pointer
foo.map(f)we could probably do: fn f(x: Ty) { ... }
foo.map(&f) |
This comment has been minimized.
This comment has been minimized.
|
Some thoughts about the proposal.
Edit: |
This comment has been minimized.
This comment has been minimized.
|
@bombless If you are wondering whether @P1start Are you saying we should get rid of the secret_unboxed_function -> function pointer conversion and replace it with @petrochenkov @Florob if you don't mind, I think both your examples would look great in the RFC to explain the coercion changes. @petrochenkov To be clear these semantic decisions would be left for the future, but I'm happy to speculate on how thing might turn out in the long run:
|
This comment has been minimized.
This comment has been minimized.
|
@Ericson2314 Yeah. In other words, a slight change in coercion rules that means that @CloudiDust @Florob Why do you need to coerce to a function pointer anyway? Assuming that @petrochenkov The anonymous function item types should already be zero-sized, but unfortunately aren’t yet (cc rust-lang/rust#19925). Regarding the |
This comment has been minimized.
This comment has been minimized.
Florob
commented
Feb 21, 2015
|
@P1start Indeed you may disregard that comment. It's a mixture of outdated information and lack of coherent thinking at the time. That said I am still generally in favour of this proposal. It adds a nice explicitness to things. |
Ericson2314
added some commits
Feb 22, 2015
Ericson2314
force-pushed the
Ericson2314:fun-vs-fun-ptr
branch
from
9afe31f
to
c712323
Feb 22, 2015
This comment has been minimized.
This comment has been minimized.
|
Ok, added the coercion rules. The response seems pretty positive here, any team member want to comment? |
This comment has been minimized.
This comment has been minimized.
|
This can be seen as an amendment to RFC 401, the Function type polymorphism section.
No need to change.
No need to change.
All function pointers are implicitly coercible to a
No need to change, but we will not be passing
No need to change. Examples: fn foo() { ... } // `foo` has a fresh and non-denotable type. (`fn() {foo}`)
fn main() {
let x: fn() = foo; // error (currently valid)
let x: &fn() = &foo; // ok, `&foo` is coerced to `&fn()`. (currently invalid)
let y: &Fn() = x; // ok, `x` is coerced to `&Fn` (a closure object),
// legal because `x` is a `&fn()` here, and `fn()` implements `Fn()`.
let z: &Fn() = foo; // error.
let z: &Fn() = &foo; // ok, `&foo` is coerced to `&Fn()`,
}I think one of the reasons that we want functions have unique types that implement closure traits directly, is to avoid virtual calls. Is this correct? EDIT: some corrections. |
This comment has been minimized.
This comment has been minimized.
|
@petrochenkov, after reading RFC 401, I think one of the reasons that "function values" (that have unique types) exist now, is because virtual calls can be avoided when function values are used as closures, while the same cannot be said about function pointers. If my understanding is correct, then it makes sense to pass function values around, and the lack of fn f() {}
// f has type fn(){f}, zero sized (ideally) and unnameable
// &f has type &fn(){f}, pointer to the zero sized and unnameable type
// fn() is the unsized and generalized function type.
// &fn() is the pointer type to the unsized and generalized function type
fn main() {
let g = f; // OK, copying a "function value".
let g = &f; // OK, g is a `&fn(){f}`
let g: &fn() = f; // ERROR: can't coerce fn(){f} to &fn()
let g: fn() = &f; // ERROR: can't create object of unsized type on stack
let g: &fn() = &f; // OK; pointers to concrete functions coerce to pointers to generalized function
} |
This comment has been minimized.
This comment has been minimized.
|
On Wed, Mar 04, 2015 at 03:04:47AM -0800, Huon Wilson wrote:
Thanks huon, I was going to ask for the same. At this point, I can I think my preferred proposal is as follows:
I believe this is more-or-less what @huonw presented as "option 2", |
This comment has been minimized.
This comment has been minimized.
|
I was thinking about this in the shower and I realized that what I wrote doesn't actually work. In particular, the use of an unsize coercion doesn't make sense -- it only makes sense if we assume that the only pointers of type
This is basically exactly what we have today except that the type of a fn pointer is I think that going through this exercise leaves me more-or-less where I was before, but perhaps mildly more negative. The use case being addressed here feels marginal -- we're making the implementations of a dynamic linking utility library or jit a smidgen more safe. (Am I missing some other advantage?) I guess it ultimately depends on whether you prefer to see |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis I think "pointers should look like pointers" outweighs the disadvantage, especially when there can be function pointers that have non-static lifetimes in the future. I notice that you assumed the I'd prefer (I expect |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis One issue that I see with the "custom type implementing a In other words, its not actually possible right now to define a custom function pointer type that can be used as By having function types actually just describe the actual code, unrelated to having a pointer to it, it would be possible to off-load all that complexity to the primitve, compiler-provided function types: |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis, I realized that I might have missed a point of your posts. Were you implying that even if we wouldn't do If this is the case, then I'd settle with simply doing |
This comment has been minimized.
This comment has been minimized.
|
Sorry everybody, I've been busy with school. I'll try to fix this up this weekend---and go with the proxy approach. @Kimundi Excellent point @nikomatsakis In case you missed it, I mentioned that (using your identifiers)
|
This comment has been minimized.
This comment has been minimized.
|
@Ericson2314, I suspect dereferencing to unsized types need special-casing anyway, so if That said, @nikomatsakis, I wonder why |
This comment has been minimized.
This comment has been minimized.
|
On Thu, Mar 05, 2015 at 06:24:58PM -0800, Richard Zhang wrote:
I was implying that, but I think I was wrong. I thought about it some |
This comment has been minimized.
This comment has been minimized.
|
On Thu, Mar 05, 2015 at 11:35:36PM -0800, Richard Zhang wrote:
To be clear, any instance of type |
This comment has been minimized.
This comment has been minimized.
Even though I wrote "to be clear", I think I was anything but. |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis, thanks, so actually you were to mean: Currently, as (So, And most importantly |
This comment has been minimized.
This comment has been minimized.
|
On Fri, Mar 06, 2015 at 04:57:41PM -0800, Richard Zhang wrote:
Yes.
Yes. |
This comment has been minimized.
This comment has been minimized.
|
So here is a summary of what we may want to do (assuming that we are going the "functions are almost statics" route):
Note:
There seems to be some asymmetry. @nikomatsakis, will |
This comment has been minimized.
This comment has been minimized.
They should work. Although, it may require the planned changes that ensure that |
This comment has been minimized.
This comment has been minimized.
|
Here is a summery of what I am thinking (based off of @nikomatsakis and @CloudiDust's:
|
This comment has been minimized.
This comment has been minimized.
For |
This comment has been minimized.
This comment has been minimized.
|
@CloudiDust I think I see what you mean. While impls on the unsized functions are better for trait objects, impls on the pointers are better for calling polymorphic functions. Frankly though, I see the problem not specific to the closure traits, but more broadly part of a trade off between supporting in polymorphic code non-Copy types (or values that could be coppied but should be mutated in place) and cutting down on extraneous indirection. The controversy of whether iterators should be Copy, and the ByRef adapter iterator I'd wager stems in part from this. An associated type defined to a borrowed pointer for non-copy types and a the type itself non copy ones would help in many places in Rust. The counter-argument is optimization should do most of that work, and it would make a whole lot of a code much less readable. And of course, negative bounds are needed to implement this. Probably the best lower-impact solution is to make some blanket impls. This would also make "reusing" sugar-made closures more ergonomic, to give an example of a different benefit. |
This comment has been minimized.
This comment has been minimized.
I think you mean:
Right? I'm thinking, generally, for non-
I think "blanket impls" would do nothing? The impls would delegate to the underlying functions. Also, would you please update the RFC? If you are still busy I'd like to help. |
This comment has been minimized.
This comment has been minimized.
|
Most of what I see here looks good, but there's a certain detail I've been pondering on - the use of "unsized" terminology. All our (current) DSTs keep around enough information to query size and alignment, so we could have a Then "unsizing" can be defined as One might also wonder what semantics would manual implementations of trait Array<T> { const LEN: usize; }
// "Just" needs generics over values.
impl<T, const N: usize> Array<T> for [T; N] { const LEN = N; }
impl<T> Sizeable for Array<T> {
fn size_of(&self) -> usize { self.LEN * size_of::<T>() }
fn min_align_of(&self) -> usize { min_align_of::<T>() }
}
// This is the tricky part, that might require backwards incompatible
// changes to Drop.
impl<T: Drop> Drop for Array<T> {
fn drop(&move self) {
for _ in self.into_iter() {}
}
}I didn't mean to post that in its entirety, but there you go. More creative usecases left as an exercise for the reader. |
This comment has been minimized.
This comment has been minimized.
|
@CloudiDust Yeah that sounds good. A simpler way to look at the problem than my ranty post above is that in many cases we consume of an abstract type bounded on a trait that only borrows self. (e.g. consuming a FnMut in Also, size isn't important because it it trivial to always compile pass-by-value to pass-by-reference (we already do that) for specific, while the converse breaks code that depends on the pointer value itself (e.g. pointer equality). @eddyb Yeah I want that for HAMTs, where traditionally nodes has a sparse array of a dynamically sized array with a bitmap to show which elements are present to save space. The size is thus the hamming weight of the bitmap. When @nikomatsakis said "... with an "auxiliary data" type of |
This comment has been minimized.
This comment has been minimized.
|
There is no mechanism in place and such a type is not valid today, as all types are required to allow their size and alignment to be queried, dynamically in the case of DSTs. |
This comment has been minimized.
This comment has been minimized.
|
@Ericson2314, would you please update the RFC soon? If you are still busy, I'd like to post an alternative RFC tomorrow (local time) to describe what we have come up in the discussions. The beta is approaching fast, so I think we should move forward now. |
This comment has been minimized.
This comment has been minimized.
|
@CloudiDust Yeah sorry. I've been busy, and for the upcoming week I won't have internet access, so I think you better do that. Thanks for taking the initiative! |
This comment has been minimized.
This comment has been minimized.
|
@Ericson2314, no problem and thanks for this RFC. The new RFC is #996. |
This comment has been minimized.
This comment has been minimized.
|
Should we close this RFC in favor of #996? |
Ericson2314 commentedFeb 17, 2015
Rendered