What is hyper::server::unnameable::Opaque?


#1

I have got the following error message from the compiler:

118 | pub fn run(address: SocketAddr, network: String, api_url: String, api_key: String) -> errors::Result<()> {
    |                                                                                       ------------------ expected `server::serde::export::Result<(), errors::Error>` because of return type
...
135 |     core.run(server).chain_err(|| "launching the server")
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found struct `hyper::server::unnameable::Opaque`
    |
    = note: expected type `server::serde::export::Result<(), _>`
               found type `server::serde::export::Result<hyper::server::unnameable::Opaque, _>`

The fix for this is to map unnameable::Opaque to void result. However, I am not sure if it is the right thing to do? Maybe I should handle Opaque somehow instead of discarding? Also, I could not find this identifier in the hyper docs, where is documented?


#2

It was introduced in https://github.com/hyperium/hyper/commit/39cf6ef7d26b3d829ec19fb1db176e8221170cb3. It looks to be a placeholder type that’s the Item for Connection's impl of Future. The idea being that hyper can change the return type in the future without breaking clients. You can map it away to (), as you’ve done, without any issues.


#3

It would be good to have it published in the documentation. I have found it by browsing the source code of hyper.


#4

I had to look at the code as well. Based on your last few questions/posts on hyper, it seems like it’s undergoing some notable API changes. Maybe once that’s finished the docs will be updated.

@seanmonstar would be able to comment authoritatively.


#5

This is exactly it. We think it might be nice for the future to resolve to the underlying IO object, but are not certain yet of the implications of that, or if maybe a special combinator on hyper::server::Connection should be that instead. By making Opaque implement nothing, and be unnameable, we can change the type to whatever in the future and it shouldn’t break anyone.

I sympathize with it not being the in the docs, but that’s a side-effect of it making it unnameable (meaning that no one can import hyper::server::unnameable::Opaque, so we can change the name). Perhaps the Connection type itself could gain a notice about it’s yield type, to that effect?


#6

Thanks for chiming in @seanmonstar.

Relatedly, there was another recent thread that @avkonst had about a replacement for bind_connection (which started emitting deprecation warnings), which we deduced to be serve_connection. It would be better UX if the deprecation message mentioned a replacement API and/or the hyper docs did. In general, glancing at the commit history of hyper suggests there’s quite a bit of API churn going on. It might be nice to guide users on what they should be using if certain APIs change in some visible (to them) way. I’m not sure what the best place for these notes would be (rust/API docs and/or deprecation messages and/or hyper’s website/tutorials), but something should probably be placed somewhere :slight_smile:.


#7

Yes, it would be great to have and considered to be the best practice to declare an alternative when things are deprecated.


#8

Also, I noticed one other “strange” thing with the deprecation message. I appreciate if you could explain it too. I had my software version (let’s say version X) being built without warnings. It had declaration for hyper dependency as hyper = "0.11.2". I evolved my software a little bit (let’s say to version Y) and maybe cleaned up the target folder, but I did not change hyper version dependency. And unexpectedly, version Y started showing the deprecation warning… It suggests that something triggered refresh of the hyper sources although I have not changed the hyper version in Cargo.toml file? How can it be?


#9

Cargo uses semver versioning scheme in this case. You can see how to specify dependencies’ versions, and what they mean in terms of upgrades, here.


#10

Right. Thanks. It is not the same as it is in sbt. In rust such declaration leads to implicit patch level update and build results using different versions of libraries depending on when you build your repo.

It seems I need to lock down versions using something like “=0.11.2”… or… In fact, I do not care what actual version is pulled for my “current” build, it may be newer than what is declared in my toml file. However, it must be the same version as what was used to build the “last” successful / verified / passed commit, which is a baseline for my “current” work. I would like to make sure that an upgrade to newer version is a controlled step and the same checkout version is built with the same versions of dependencies on all machines, everywhere, every time. I noticed there is Cargo.lock file, which seems takes actual versions being used… May this file help somehow to lock down versions without specifying “= 0.11.2” in toml file? What are my options?


#11

Indeed, the purpose of the Cargo.lock file is to keep track of which dependencies versions you are currently using. Which is why it is considered good practice to add Cargo.lock to version control when building end-user applications. Adding it to libraries is more frowned upon due to the increased risk of dependency hell.

As long as a Cargo.lock is present, cargo will only update your dependencies if you explicitly run “cargo update” . It will do so in a manner that respects the constraints stated in Cargo.toml (semver-compatible with the specified version by defaut, you can change it with various prefixes).


#12

Interestingly, I had the lock file, but the update was triggered some how… I played with cargo-release tool, maybe it caused the change… Ok. Does not matter now probably, will observe the situation in the future.