Conditional compilation in Rust 0.10? - rust

I have been using 0.10 and recently setup a build of nightly to experiment with Box and friends.
Now I have code for 0.10 using ~str and code for pre0.11 using String because of to_owned being obsolete. I thought I could do this:
#[cfg(rust_version = "0.10")]
fn my_old_func() -> Option<~str> {
}
#[cfg(not(rust_version = "0.10")]
fn my_old_func() -> Option<String> {
}
And pass --cfg rust_version:0.11 during compilation. But the compiler still chokes on the now removed ~ operator. Is there a way to have code that works under both 0.10 and the as yet unreleased 0.11 using conditional compilation or some other mechanism?
I guess I could fall back to using cpp and #ifdef but that seems like stepping out of the Rust mindset.

No, there is nothing you can do about this.
Our typical recommendation is not to use 0.10 but to stick with nightlies.

Related

Code and feature conditional on Rust version or specific compiler feature

Rust 1.63 stabilized const std::sync::Mutex::new(). Till date to have global flag preventing parallel execution would look like (example):
use once_cell::sync::Lazy;
static GLOBAL_FLAG: Lazy<Mutex<()>> = Lazy::new(Default::default);
while in since 1.63 it can look like this and the dependency isn't required anymore in Cargo.toml:
static GLOBAL_FLAG: Mutex<()> = Mutex::new(());
Now, the question is how to make use of both conditionally, depending on either:
which version of the compiler is used to build the crate
or
whether the feature is stable (I struggle to find it's correct name at the moment)?
I was looking at cfg_rust_features crate, but the part where dependency isn't required by Cargo.toml remains mystery. Any hints welcome.
This is typically accomplished by:
Detecting the Rust version (using rustversion/rustc_version) or the required feature (using autocfg),
Making the fallback dependency (once_cell) optional, and
Emitting an error if the required Rust feature is missing and the fallback dependency is also missing.
For example, using rustversion:
#[rustversion::since(1.63)]
mod private {
use std::sync::Mutex;
pub static GLOBAL_FLAG: Mutex<()> = Mutex::new(());
}
#[rustversion::before(1.63)]
#[cfg(feature = "once_cell")]
mod private {
use once_cell::sync::Lazy;
use std::sync::Mutex;
pub static GLOBAL_FLAG: Lazy<Mutex<()>> = Lazy::new(Default::default);
}
#[rustversion::before(1.63)]
#[cfg(not(feature = "once_cell"))]
compile_error!(
"The \"once_cell\" feature must be enabled for Rust compiler versions prior to 1.63."
);
When compiling on 1.62 without once_cell, this emits
error: The "once_cell" feature must be enabled for Rust compiler versions prior to 1.63.
--> src/main.rs:18:1
|
18 | / compile_error!(
19 | | "The \"once_cell\" feature must be enabled for Rust compiler versions prior to 1.63."
20 | | );
| |_^
Note: the use of mod private seen above is not strictly necessary, but it reduces the number of attributes and ensures that the relevant compile error is the only error emitted. You can pub use private::GLOBAL_FLAG to put it back in scope.

In Rust how do I pass a diverging function as parameter to another function

Non-diverging functions work fine
fn test(f: &Fn() -> u8) {}
But I can't accept a diverging function like this
fn test_diverging(f: &Fn() -> !) {}
I get the following error
error[E0658]: The `!` type is experimental (see issue #35121)
--> examples/two_tasks.rs:44:31
|
44 | fn test_diverging(f: &Fn() -> !) {}
| ^
Looking at issue #35121 I could see how that might fix it but in the mean time is there a work around?
! in some contexts is still experimental, which means it's not available on the stable compiler (1.33 as of today). You can use it on the nightly compiler, but you have to opt-in explicitly to feature(never_type):
#![feature(never_type)]
fn test_diverging(f: &Fn() -> !) {}
(playground)
Be aware this means the feature may yet change before stabilization, so you're accepting the risk that a future compiler version will break your code.
See also
What is a crate attribute and where do I add it?
What is the stabilization process?
How to use nightly: Is it possible to have multiple coexisting Rust installations?
Using the never type in functions and function pointer types is already stable. So if you don't need to use the Fn trait, you can just use this:
fn test_diverging(f: fn() -> !) {}
// ^ note the lowercase f
test_diverging(|| panic!("ouch"));
That way you can't pass closures that reference their environment, but non-capturing closures and standard functions work fine.
If you want to stay on stable, you can use Void (basically an enum with no variants, which can't be constructed without unsafe) as a workaround.
Playground link
To use unstable feature you need to use nightly toolchain and active the desired unstable feature.
#![feature(never_type)]
fn test_diverging(f: &Fn() -> !) {}
See also:
What is a crate attribute and where do I add it?

How to properly reference the same code as a dependency of a dependency?

I've got a toy project that's using the Amethyst game engine. I'm trying to write my own System for collecting user input, similar to the FlyMovementSystem and ArcBallRotationSystem they have implemented here.
It appears the right way to go about collecting mouse movements is via an EventChannel<Event>, where Event comes from the winit crate, which Amethyst depends on, but does not re-export.
What's the "right" way to reference the same winit::Event that Amethyst does?
Should I add winit to my Cargo.toml file? If so, what is the recommended way to specify the version? (Is there some keyword I can use instead of a specific version number, to allow me to "inherit" the dependency from Amethyst?)
Is referencing sub-dependencies actually discouraged? If so, what should I be doing instead?
There is currently no great solution to this problem. The best workaround is to add a direct dependency on the same version of the transitive dependency:
[dependencies]
foo = "0.1"
bar = "0.2" # `foo` depends on bar 0.2 and we need to keep these in sync
You can use tools like cargo tree to manually identify the versions needed by foo and keep your Cargo.toml up to date. I highly recommend adding a comment specifying why you've picked a specific version.
If the crate is very difficult to use without also using the underlying dependency alongside it, I'd also encourage you to file an issue with the parent crate to request that they re-export what is needed. A good example of this is the Tokio crate, which re-exports large chunks of the futures crate.
Similar to your idea, I proposed having a way to use the same version as a dependency. Instead, the Cargo team opted to add the distinction of public and private dependencies. While this will be a better solution from an engineering point of view, very little progress has been made on the implementation of it.
See also:
Why is a trait not implemented for a type that clearly has it implemented?
I'm leaving #Shepmaster's answer as the accepted one, as it answers the general question I was going for. But thanks to a gentle push from #trentcl, in case anyone found this question specifically for its relation to Amethyst, here's what I ended up doing.
Don't try to get the winit::Events at all.
When you attach an InputBundle<AX, AC> to your GameData, it sets up an InputSystem<AX, AC>, which re-publishes winit::Events in the form of InputEvent<AC>.
It does this by setting up an EventChannel<InputEvent<AC>> as a Resource, which you can access via the Read type in the ECS system. EventChannels and their usage are explained in the Amethyst Book.
I've since switched to a different approach for handling my user input, but here's roughly what it looked like (note: Amethyst a little after v0.10.0):
pub struct MouseMovementSystem {
reader: Option<ReaderId<InputEvent<()>>>, // AC = ()
}
impl<'s> System<'s> for MouseMovementSystem {
type SystemData = (
Read<'s, EventChannel<InputEvent<()>>>,
/* and others */
}
fn run(&mut self, (events, /* and others */): Self::SystemData) {
let foo = events.read(self.reader.as_mut().unwrap())
.yadda_yadda(/* ... */); // event processing logic
do_stuff(foo);
}
fn setup(&mut self, res: &mut Resources) {
use amethyst::core::specs::prelude::SystemData;
Self::SystemData::setup(res);
self.reader = Some(res.fetch_mut::<EventChannel<InputEvent<()>>>().register_reader());
}
}

Does String Literal Slice Usage with String references is Valid?

I have the following code block and it works as intended with no problem:
fn literal_taker(literals_slice: &[&str]){
println!("{:?}",literals_slice);
}
fn string_taker(string_value: String){
literal_taker(&[&string_value]);
}
fn main() {
let string_value = String::from("Hello");
string_taker(string_value);
}
Here, I pass the reference of String as a slice and it is compiling with no error and no clippy issue.
But the problem is, It is shown as warning in Clion Rust plugin:
Is it a bug of an plugin or am I doing something bad practice in Rust?
Playground
CLion Rust Plugin Version: 0.2.0.2106-182
The code does compile as written, as the playground clearly demonstrates. Therefore it is a bug in the IDEA Rust Plugin.
Unlike most other Rust plugins that use the Rust Language Server, which uses code from the compiler, and therefore generally provides diagnostics consistent with what the compiler will, IntelliJ IDEA has its own validator, which might get things wrong.

No method 'entries_mut' when unarchiving a file with the tar crate?

I am trying to use the flate2 and tar crates to iterate over the entries of a .tar.gz file, but am getting type errors, and I'm not sure why.
Here is my code (and yes, I know I shouldn't use .unwrap() everywhere, this is just POC code):
extern crate flate2; // version 0.2.11
extern crate tar; // version 0.3
use std::io::Read;
use std::fs::File;
use flate2::read::GzDecoder;
use tar::Archive;
fn main() {
let file = File::open("/path/to/tarball.tar.gz").unwrap();
let mut decompressed = GzDecoder::new(file).unwrap();
let unarchived = Archive::new(decompressed);
let entries_iter = unarchived.entries_mut();
}
This gives me the error error: no method named 'entries_mut' found for type 'tar::Archive<flate2::gz::DecoderReader<std::fs::File>>' in the current scope.
GzDecoder::new is returning a DecoderReader<R>, which implements Read as long as R implements Read, which File does, so that should be fine. Archive<O> has different methods depending on what kind of traits O implements, but in this case I am trying to use .entries_mut(), which only requires O to implement Read.
Obviously I am missing something here, could someone help shed some light on this?
Oh man, this is tricky. The published documentation and the code do not match. In tar version 0.3.2, the method is called files_mut:
extern crate flate2; // version 0.2.11
extern crate tar; // version 0.3
use std::fs::File;
use flate2::read::GzDecoder;
use tar::Archive;
fn main() {
let file = File::open("/path/to/tarball.tar.gz").unwrap();
let decompressed = GzDecoder::new(file).unwrap();
let mut unarchived = Archive::new(decompressed);
let _files_iter = unarchived.files_mut();
}
This commit changed the API.
This is a subtle but prevalent problem with self-hosted Rust documentation at the moment (my own crates have the same issue). We build the documentation on every push to the master branch, but people use stable releases. Sometimes these go out of sync.
The best thing you can do is to run cargo doc or cargo doc --open on your local project. This will build a set of documentation for the crates and versions you are using.
Turns out that the published documentation of tar-rs was for a different version than what was on crates.io, so I had to change .entries_mut to .files_mut, and let files = to let mut files =.

Resources