-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Cannot use function pointer with dynamic dispatch without identity cast #86654
Comments
I agree that the error message could be improved, but just to explain what happens here: The cast is not the identity transformation, because
|
Oh, interesting, thanks for the explanation. Yeah, the error message can certainly be improved here, since it doesn't actually show any meaningful difference between the types. I also wonder if this would be a good candidate for allowing implicit coercion, rather than requiring it to be explicit as is the case currently — is there ever a case where this coercion is undesirable? |
I think a simple coercion isn't enough here, because you have to make the reference point to something: pub fn bar(_: &fn()) {}
pub fn baz() {
fn good() {}
// All good:
bar(&(good as fn()));
// No good:
bar(&good);
}
If you remove the references in lines 1, 7 and 10, it compiles. |
Oh, right, Stepping back for a second, I also wonder what the right way to support the " |
I think constants do have memory addresses, just not necessarily stable ones. See the reference:
But they need to have some address, because you can create references to them.
What is the use case here? I can only imagine that you might want to add a method |
I think that's only in the contexts of other constants referring to them, as suggested by the following text:
This ties back to the fact that constants are inlined everywhere:
Which means that while I suppose they may have an address in any given place, they don't have one in general. At least I don't see how they could?
It's not prohibitive really, though very slightly unfortunate from a performance perspective. I'm more looking at this from the point of ergonomics — it seems unfortunate to have to explicitly coerce a function definition to a function pointer, and to have to coerce a function pointer to a fat pointer for a given |
Thanks for the interesting discussion by the way, @jonhoo!
I see your point, but I think we both mean more or less the same thing: Constants are usually inlined, so they conceptually don't have an address in memory, but if you take a reference to a constant, it has to point somewhere, so the compiler will create some (conceptually) ad-hoc storage location for the constant and make the reference point to that, and if you take another reference to the same constant, it may or may not point to the same storage location.
I think there may be another reason why the data pointer can't be the function pointer directly (i.e. there has to be an indirection): Storing the function pointer directly in the fat pointer would mean passing it by-value, i.e. the
Also, of course, if you call a trait method that takes a fn a() { println!("a"); }
fn b() { println!("b"); }
trait Foo { fn foo(&mut self); }
impl Foo for fn() {
fn foo(&mut self) {
*self = b;
}
}
fn main() {
let mut fn_ptr = a as fn();
(&mut fn_ptr as &mut dyn Foo).foo();
fn_ptr(); // prints "b"
} |
Likewise! Hmm, yeah, that's a good point that the caller expects to get a reference to a function pointer, and not "just" a function pointer. I suppose in my case the way to work around that would be to have the trait take |
Hit a few more examples of these errors being relatively unhelpful yesterday (playground): trait Foo {
fn bar(&mut self);
}
impl Foo for fn() {
fn bar(&mut self) {
(*self)()
}
}
fn takes_foo(_: impl Foo) {}
fn is_foo() {}
fn main() {
takes_foo(is_foo);
// this gives the error
//
// error[E0277]: the trait bound `fn() {is_foo}: Foo` is not satisfied
// --> src/main.rs:16:15
// |
// 18 | takes_foo(is_foo);
// | --------- ^^^^^^ the trait `Foo` is not implemented for `fn() {is_foo}`
// | |
// | required by a bound introduced by this call
// |
// = help: the following implementations were found:
// <fn() as Foo>
//
// but the help doesn't make it clear that the `{is_foo}` part is relevant,
// and that this can be fixed with a cast to a FnPtr:
takes_foo(is_foo as fn());
takes_foo(|| {});
// this gives the error
//
// error[E0277]: the trait bound `[closure@src/main.rs:34:15: 34:20]: Foo` is not satisfied
// --> src/main.rs:34:5
// |
// 33 | takes_foo(|_: &()| true);
// | ^^^^^^^^^ the trait `Foo` is not implemented for `[closure@src/main.rs:34:15: 34:20]`
//
// which doesn't even mention that the closure can be cast to a FnPtr, which
// _would_ satisfy the bound:
takes_foo((|| {}) as fn());
// (note also the extra () needed to avoid the cast being applied to `{}`)
} |
I have also been struggling with this issue for some time now. My use case is a bit specific, I use impl IntoFnPtr<Self> for fn(i32) {
type Type = fn(i32);
fn into_ptr(self) -> FnPtr<Self::Type> {
let table_index = self as usize; // The Wasm table index belonging to this function.
FnPtr(Phant table_index omData)
}
} I can't use But, because this coercion doesn't happen automatically the whole codebase is full of |
I tried this code (playground):
I expected to see this happen: the code should compile.
Instead, this happened: compilation fails with the error
This feels wrong, since the cast appears to be applying the identity function, but I could be missing something about how this is intended to work. Changing
dyn Drop
to(dyn Drop + 'static)
(as the error message hints at) does not make a difference.Meta
rustc --version --verbose
:The warning also occurs on nightly.
The text was updated successfully, but these errors were encountered: