Skip to content
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

Bare function do not implement trait unless explicitly casted #21086

Closed
Marwes opened this issue Jan 13, 2015 · 5 comments
Closed

Bare function do not implement trait unless explicitly casted #21086

Marwes opened this issue Jan 13, 2015 · 5 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-typesystem Area: The type system

Comments

@Marwes
Copy link
Contributor

Marwes commented Jan 13, 2015

It might be that this is intended since it seems that each function is treated as its own type (and by casting you can get the common function type that the trait is implemented for). I'd still argue it is confusing behavior so maybe the error message should be improved in that case?

(In this case we could do a blanked implemantion over one of the function trait but that does not work for my case, atleast not without #[old_impl_check])

trait Test { }
impl Test for fn () { }

fn tester<T: Test>(_: T) { }

fn void() { }

fn main() {
    tester(void);//Test is not implemented for fn () {void}
    tester(void as fn ());//Works
}
@Marwes Marwes changed the title Function do not implement trait unless casted Bare function do not implement trait unless explicitly casted Jan 13, 2015
@kmcallister kmcallister added A-typesystem Area: The type system A-diagnostics Area: Messages for errors, warnings, and lints labels Jan 14, 2015
@jansegre
Copy link

I'm getting the same issue, didn't use to have it a short before alpha, can't find out which rev I was when it worked.

That case seems simpler I had boiled mine down to:

#[allow(unstable)]
extern crate libc;
use libc::{c_void, c_int};

struct State;

trait Push {
    fn push_to(state: &mut State, value: Self);
}

type CFun = extern fn(s: *mut c_void) -> c_int;
impl Push for CFun {
    fn push_to(_state: &mut State, _value: CFun) {
        // do something
    }
}

fn main() {
    extern fn foo(_s: *mut c_void) -> c_int { 1 }
    let mut st = State;
    Push::push_to(&mut st, foo);  // error: trait `Push` not implemented for ...
    Push::push_to(&mut st, foo as CFun);  // works!
}

@Marwes
Copy link
Contributor Author

Marwes commented Jan 27, 2015

Link to the pull request where this changed: #19891.

@jansegre
Copy link

Oh, so this is intended, should be closed then.

@jansegre
Copy link

This is a side effect of the coercions rfc but I think it's terrible.
I don't get why every function must have it's own type now, that reasoning is not on the rfc. I'm trying to hunt it down.

Even though I think it should be possible for the coercion to be implicit.

The rationale is that it should be consistent with plain/primitive data types:

There are these traits: Int, Float, Fn, ..., and there are these concrete types for instance: i8, i32, f64, fn(), fn() -> i32, ...
When you know the concrete type you can implement some functionality that takes into account the structure of the type

  • for instance you can take some i8 and store it compressed which can be decompressed to get the original i8 back
  • the same goes for fn() types, you know they're a concrete type and thus you can transmute it to a pointer store that pointer on a location and have an extern fn() reconstruct that pointer with the signature of the function (which you have) and call it, that works for dispatching functions from rust libraries to ffi libraries.

Now, when your primary interface for functions are traits it gets a lot harder to do lower level stuff because you don't know the underlying structure for the function so your interaction with it is limited by what the traits let's you do, which is call the function, bah.

I'm not saying Fn traits are bad, I think they're great, but it has gotten much more difficult to work with fn() types after they became unique for each function with apparently only a few coercions. I think it's fair for one to be able to implement a certain trait for a fn() but not for a Fn() because that certain impl depends on the concrete type, and the user should not have to coerce a regular function to fn() and the user should also be able to impl that certain trait for whatever type they want.

@SSheldon
Copy link
Contributor

I think this could be considered a bug. Functions are coerced to pointers when there aren't generics:

fn call(f: fn()) {
    f()
}

fn foo() {
    println!("foo");
}

fn main() {
    call(foo);
}

But if you use generics, they are no longer coerced:

trait Callable {
    fn call(self);
}

impl Callable for fn() {
    fn call(self) {
        self();
    }
}

fn call<F: Callable>(f: F) {
    f.call();
}

fn foo() {
    println!("foo");
}

fn main() {
    call(foo); // error: the trait `Callable` is not implemented for the type `fn() {foo}`
}

@nikomatsakis and @eddyb (since you've both worked on this previously), do you think this issue is something we could fix and should be reopened?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-typesystem Area: The type system
Projects
None yet
Development

No branches or pull requests

5 participants