Use trait from crate in the same lexical scope


#1

Inside a particular lexical scope (not a module) I have an extern crate declaration. How do I use a type from that crate?

I tried the following:

fn main() {
    extern crate core;
    use core::any::Any;
}

The compiler diagnostic says “Did you mean `self::core::any`?” which sounds like a reasonable thing to try.

fn main() {
    extern crate core;
    use self::core::any::Any;
}

Now the compiler diagnostic says “Did you mean `self::self::core::any`?” which sounds suspicious.

fn main() {
    extern crate core;
    use self::self::core::any::Any;
}

The compiler diagnostic says “Maybe a missing `extern crate self`?” which obviously won’t help.

This seems like something that should be possible given that both of the following are possible:

  •   extern crate core;
      fn main() {
          use core::any::Any;
      }
    
  •   fn main() {
          extern crate core;
          let _: core::any::Any;
      }
    

Is there a way I can get this to work without moving the extern crate declaration? If not, is there a related rust-lang issue?


#2

You can’t.

The problem is that use paths are absolute: they start relative to the root module. The inside of your function is not the root module. self refers to the containing module, which is also not the inside of your function. Nor is super.

I’m not aware of an issue for this. To be honest, it’s usually easy enough to work around by just putting the extern crate at module scope.


#3

Would it make sense to disallow/discourage extern crate in non-mod items then?

And you can always wrap the function in its own mod { } if you’re concerned about namespaces.


#4

It’s not like you can’t use them, you just can’t use them.


#5

Got it. Thanks for the quick response.

I am not one of the “usually” cases. In Serde we recently started expanding

#[derive(Serialize)]
struct MyStruct {
    // ...
}

… into …

const _IMPL_SERIALIZE_FOR_MyStruct: () = {
    extern crate serde as _serde;
    impl _serde::de::Serialize for MyStruct {
        // ...
    }
};

… in order to support cases where the extern crate serde is not in the top-level module (see serde-rs/serde#159). We can’t use the module’s own extern crate serde because (a) we don’t know whether it was imported as “serde” or as some alias, and (b) we don’t know the absolute path to it during macro expansion. We can’t add our own extern crate serde at module scope as @DanielKeep suggested because they would collide when multiple structs in a module have #[derive(Serialize)]. And we can’t put our impl into its own mod as @birkenfeld suggested because there is no way to import the types that may be used in the struct definition (see again the ticket I linked). Finally, I can’t “use” the type without use-ing it as @DanielKeep suggested because the type is a trait and I need to bring methods from the trait into scope in order to call them on an instance. The exact situation looks like:

trait Error {
    fn f();
}
trait MapVisitor {
    type Error: Error;
}
fn f<V>() where V: _serde::de::MapVisitor {
    use _serde::de::Error; // if only
    V::Error::f();
}

I will continue brainstorming ways to work around this. Currently it is blocking serde-rs/serde#293.


#6

Clearly, you should just switch to using custom_derive and parse-macros, and then you can use $crate. Heck, the repo for the latter already contains a stable Serialize derive macro:stuck_out_tongue:


#7

Can’t you use UFCS instead of brining the trait into scope?


#8

V::Error::f() is already UFCS because it is an associated function not a method :slight_smile: .

I got this working by doing <V::Error as _serde::de::Error>::f(). Thanks for the help all. It seems that the limitation of not being able to use the trait is not valuable to address given the many workarounds.


#9

@DanielKeep I applaud the effort that went into the derive_serialize macro but I can’t begin to imagine the hacks upon hacks that would be necessary to support attributes or support non-trivial generics as comprehensively as serde_codegen does: (1) (2) (3).


#10

Well, I’m unsure if it’d be more hacky than crowbaring an entire copy of libsyntax into the build process or being stuck on a nightly compiler for who-knows how long, or less.

But, serious question: how often do people need the custom attributes? There have been one or two cases in the past where I would have liked some extra control, but nothing I needed. If I can hit the core 90% of use cases with a 100% stable macro, I’d call that a win.


#11

Hopefully at some point in the next decade, compiler plugins will be made stable. Until then I agree that derive_serialize is nifty and valuable.

From a few rough searches, 80% of serde attributes on GitHub are the rename attribute. The primary driver here is that Rust uses snake_case for field names while JSON and YAML typically use lowerCamelCase.

Here are the numbers, although I can’t say the extent to which these fall into the category of “liked” vs “needed.”


#12

rename, default, deny_unknown_fields and maybe skip_serializing (I’m not sure what it does) look doable. Supporting all the field ones simultaneously might take some trickery.

skip_serializing_if is out out of the question, since it quotes the expression.

derive_serialize was really just meant as proof that the macros I was writing had the necessary power to pull it off, but if there’s interest, I can look at whipping them into shape. The major reason I don’t use serde is because it’s a huge pain on stable.