Hacker News new | ask | show | jobs
by no_protocol 3400 days ago
I am not sure if this directly applies to your post, but it came to mind when I read the section about `extern crate`. It seems like the Cargo system is relied upon by almost all Rust users, and I am not sure if the following is an ergonomics problem or a lack of understanding on my part.

A couple months ago I was exploring Rust's web server capabilities after seeing the "Are we web yet?" page. I decided to try out the `iron` package.[0]

I was quickly able to serve some basic content, but I wanted to add some headers to the response.

I was able to `use iron::headers::Allow` to add an Allow header.[1]

Next, I wanted a Link header. Link wasn't available but I could get around that by defining a custom header with the `header!` macro. Unfortunately, I couldn't figure out how to get the `header!` macro for custom headers without `#[macro_use] extern crate hyper` and adding `hyper` to the Cargo.toml file.[2]

Then I wanted a `Vary` header. I was able to get that in with `use iron::headers::Vary`, but I couldn't actually create one yet! In order to create my `Vary::Items` header, I needed to also `use unicase::UniCase` and add `unicase` to my Cargo.toml.[3][4]

So within an hour or so of starting the project, my explicitly listed dependencies had grown from just `iron` to include two additional dependencies. The iron package already relies upon hyper which already relies upon unicase.

Here are some questions I am still left with. Would love any responses.

Is it possible for me to use the pieces described above without explicitly listing these crates? If not, why do I need to declare hyper as a dependency when iron is already using it? Perhaps I don't need to, and I was just unable to figure out how to get the `header!` macro from iron directly. My initial expectation was that iron would either wrap or expose every part of hyper that I might need. The same goes for hyper not allowing me to just use the same unicase it relies upon.

How am I supposed to get the correct version of hyper and unicase to match with the ones that my version of iron was sent with? Do I have to go look them up? Can use the latest version of `hyper` even if `iron` is a few versions behind? What version should I be specifying?

[0]: http://ironframework.io/doc/iron/

[1]: http://ironframework.io/doc/iron/headers/struct.Allow.html

[2]: https://hyper.rs/hyper/v0.9.9/hyper/macro.header!.html

[3]: http://ironframework.io/doc/iron/headers/enum.Vary.html

[4]: http://ironframework.io/doc/unicase/struct.UniCase.html

4 comments

> Is it possible for me to use the pieces described above without explicitly listing these crates?

Not unless iron exposed them directly. I haven't used it a while, so I'm not sure if it does.

> If not, why do I need to declare hyper as a dependency when iron is already using it?

There's a difference between a direct and transitive dependency, it's not going to just inherently bring transitive stuff into scope.

> Perhaps I don't need to, and I was just unable to figure out how to get the `header!` macro from iron directly.

Yeah I haven't used iron in long enough to say, but this is theoretically possible.

> My initial expectation was that iron would either wrap or expose every part of hyper that I might need.

Yes, it is possible that iron's API isn't the best here.

Macros and the importing and scoping of them is a mess in rust right now and major rewrite of the macro system is underway. Currently crates have no way to re-export macros from other crates and #[use_macros] brings all macros in the imported crate to the local crate's namespace as-is (no way to prevent possible naming conflicts)
major rewrite of the ... is underway.

That's a common response to criticisms of Rust. At this late date, that's a problem.

There are only two cases where that's been a thing. Macros, and nonlexical lifetimes.

The major rewrite for macros has already brought fruit with Macros 1.1, solving some of the more pressing issues.

The other one, nonlexical lifetimes, needed a complete overhaul of compiler internals. The main part of that work happened with MIR, but there's still work to be done. It's ongoing. This was work that was going to take a long time, and it did.

I mean, it would still have taken time. Should they have delayed 1.0 and stability for it? I don't see the benefit.

There have been plenty of quality of life features that were promised and delivered successfully. Many new ones have been discovered and are in active, visible progress, but are not implemented yet, so yes, you can always identify some pieces that are still being developed. The amount of resources is finite, but Rust is getting significantly better each year.

What else is that a response to? Macros have had this planned since before 1.0, with the current system specifically designed as a temporary workaround until it that happens.
I agree, the macro scoping is just a small part of the rewrite but it is a problem (especially ergonomically) that rust should have got right the first time around.

Right now macros don't quite feel like they are part of the core language, but instead a hack or plugin slapped haphazardly on top of the compiler.

> I am not sure if this directly applies to your post, but it came to mind when I read the section about `extern crate`. It seems like the Cargo system is relied upon by almost all Rust users, and I am not sure if this is an ergonomics problem or a lack of understanding on my part.

This is definitely true, and I don't think it's negative. You can use Rust without Cargo and crates.io, but it feels very different. It's nice that that option exists, but I wouldn't want to work that way. Any sizeable project is likely to accumulate a large number of crates.io dependencies, but having written a lot of C++ code, the alternatives are "reimplement this thing myself" or "copy paste some code", neither of which are particularly good.

Oops, I used `this` when I should have said `the following.` I have updated the line in my post above.

I wasn't suggesting Cargo being relied upon by everyone was an ergonomics issue, but instead referring to the description in the remainder of my post.

> why do I need to declare hyper as a dependency when iron is already using it?

Without commenting on the particulars of the iron API (I haven't used it), it makes sense for library authors to be able to make an explicit decision as to whether or not their dependencies are going to be a part of their backwards compatibility contract or not. You could imagine a circumstance where a library A that you used depended on some library B and wanted to switch, internally, to instead depending on library C. If library A had exposed library B to its consumers as part of a public interface, that would be a breaking change, but it wouldn't be if they hadn't.

> it makes sense for library authors to be able to make an explicit decision as to whether or not their dependencies are going to be a part of their backwards compatibility contract or not

Absolutely. But as soon as those are required in order to use your library, shouldn't they be included?

> If library A had exposed library B to its consumers as part of a public interface, that would be a breaking change, but it wouldn't be if they hadn't.

Maybe I should have refined my original post. I guess it was way too long.

The hyper crate exposes `hyper::header::Vary`. To create a `Vary::Items` in my program, I also need to `use unicase::UniCase`. See the short example at [0].

It felt odd to me that I was required to have my own dependency on unicase when it seems like an internal issue for how Hyper represents the Vary header.

I have little experience with these dependency management systems. This seems like an irrelevant detail (they look just like strings to me! why doesn't it just take strings!) requiring me to explicitly include an unrelated package. I may have no other need for the unicase package.

Is this a normal thing, though? Since unicase is required in order to use this feature of hyper, shouldn't it just come along with it? Or the Vary item could just use Strings on its public interface so the user doesn't have to go through this extra work?

[0]: https://hyper.rs/hyper/v0.8.1/hyper/header/enum.Vary.html#ex...

I believe what hyper should do there is `pub use ::unicase` in the hyper crate root, allowing you to use unicase directly through hyper - since it's a hyper dependency and you need to ensure you're using the same versions as it.
Thank you! At least I'm not the only person who believes this would be the expected behavior. I'm still half expecting someone else to pop out and explain that I'm way wrong, though.