Rust not printing to terminal - rust

rustc is not outputting anything to terminal when using println!.
Code:
fn main() {
println!("Hello, world!");
}
Running it:
me#mclaptop:~
> rustc helloworld.rs
me#mclaptop:~
>
Why does it not print anything?

rustc is the compiler of the Rust language, it just produces an executable to be run. If you want to actually see the output you must run the ./helloworld command.
You can read about it here.

rustc only compiles your code. You need to call the output binary to get it working.
Try ./helloworld or whatever the name of the output file is.

On Linux and Mac:
rustrc helloworld.rs && ./helloworld

You can simply use:
cargo run
This will compile current project and run it just with one command.

Related

How can I pass flags to rustc from a build script if they contain a space?

I am writing a Rust program with some C integration so I'm using a custom build script. In this script, I pass -L <path to library> to rustc, but this works only if <path to library> does not contain a space.
The exact line in the build.rs:
println!(r"cargo:rustc-flags= -L {}/target/sdsl/sdsl_install/lib -l sdsl -l divsufsort -l divsufsort64 -l stdc++", current_dir);
If current_dir contains a space I get this error
error: Only `-l` and `-L` flags are allowed in build script of `top_tree_compression v0.1.0 (file:///home/jan/Uni/Bachelorarbeit/Programme/Top_Tree%20Compression)`: `-L /home/jan/Uni/Bachelorarbeit/Programme/Top_Tree Compression/target/sdsl/sdsl_install/lib -l sdsl -l divsufsort -l divsufsort64 -l stdc++`
I tried to write a \ before the space to escape it but it gives me the same error. Then I tried to replace the space with %20 because in the error message the space was replaced with this, but then I get a linking error because the path is not correct.
It appears you cannot as of Rust 1.29. The source code for the current master of Cargo:
let mut flags_iter = value
.split(|c: char| c.is_whitespace())
.filter(|w| w.chars().any(|c| !c.is_whitespace()));
This naively splits the argument on any whitespace, regardless of where it occurs. This seems to be a bug or limitation of Cargo and you should look for an already-filed issue or file one yourself.
That being said, if you use the more fit-for-purpose rustc-link-lib and rustc-link-search parameters, spaces work fine:
println!(r#"cargo:rustc-link-search={}/target/sdsl/sdsl_install/lib"#, "some thing");
$ cargo run --verbose
Compiling xx v0.1.0 (file:///private/tmp/xx)
[...snip...]
Running `rustc [...snip...] -L 'some thing/target/sdsl/sdsl_install/lib'`

How to execute Rust code directly on Unix systems? (using the shebang)

From reading this thread, it looks like its possible to use the shebang to run Rust *.
#!/usr/bin/env rustc
fn main() {
println!("Hello World!");
}
Making this executable and running does compile, but not run the code.
chmod +x hello_world.rs
./hello_world.rs
However this only compiles the code into hello_world.
Can *.rs files be executed directly, similar to a shell script?
* This references rustx, I looked into this, but its a bash script which compiles the script every time (without caching) and never removes the file from the temp directory, although this could be improved. Also it has the significant limitation that it can't use crates.
There's cargo-script. That also lets you use dependencies.
After installing cargo-script via cargo install cargo-script, you can create your script file (hello.rs) like this:
#!/usr/bin/env run-cargo-script
fn main() {
println!("Hello World!");
}
To execute it, you need to:
$ chmod +x hello.rs
$ ./hello.rs
Compiling hello v0.1.0 (file://~/.cargo/.cargo/script-cache/file-hello-d746fc676c0590b)
Finished release [optimized] target(s) in 0.80 secs
Hello World!
To use crates from crates.io, please see the tutorial in the README linked above.
This seems to work:
#!/bin/sh
//usr/bin/env rustc $0 -o a.out && ./a.out && rm ./a.out ; exit
fn main() {
println!("Hello World!");
}
I have written a tool just for that: Scriptisto. It is a fully language agnostic tool and it works with other compiled languages or languages that have expensive validation steps (Python with mypy).
For Rust it can also fetch dependencies behind the scenes or build entirely in Docker without having a Rust compiler installed. scriptisto embeds those templates into the binary so you can bootstrap easily:
$ scriptisto new rust > ./script.rs
$ chmod +x ./script.rs
$ ./script.rs
Instead of new rust you can do new docker-rust and the build will not require Rust compiler on your host system.
#!/bin/sh
#![allow()] /*
exec cargo-play --cached --release $0 -- "$#"
*/
Needs cargo-play. You can see a solution that doesn't need anything here:
#!/bin/sh
#![allow()] /*
# rust self-compiler by Mahmoud Al-Qudsi, Copyright NeoSmart Technologies 2020
# See <https://neosmart.net/blog/self-compiling-rust-code/> for info & updates.
#
# This code is freely released to the public domain. In case a public domain
# license is insufficient for your legal department, this code is also licensed
# under the MIT license.
# Get an output path that is derived from the complete path to this self script.
# - `realpath` makes sure if you have two separate `script.rs` files in two
# different directories, they get mapped to different binaries.
# - `which` makes that work even if you store this script in $PATH and execute
# it by its filename alone.
# - `cut` is used to print only the hash and not the filename, which `md5sum`
# always includes in its output.
OUT=/tmp/$(printf "%s" $(realpath $(which "$0")) | md5sum | cut -d' ' -f1)
# Calculate hash of the current contents of the script, so we can avoid
# recompiling if it hasn't changed.
MD5=$(md5sum "$0" | cut -d' ' -f1)
# Check if we have a previously compiled output for this exact source code.
if !(test -f "${OUT}.md5" && test "${MD5}" = "$(cat ${OUT}.md5)"); then
# The script has been modified or is otherwise not cached.
# Check if the script already contains an `fn main()` entry point.
if grep -Eq '^\s*(\[.*?\])*\s*fn\s*main\b*' "$0"; then
# Compile the input script as-is to the previously determined location.
rustc "$0" -o ${OUT}
# Save rustc's exit code so we can compare against it later.
RUSTC_STATUS=$?
else
# The script does not contain an `fn main()` entry point, so add one.
# We don't use `printf 'fn main() { %s }' because the shebang must
# come at the beginning of the line, and we don't use `tail` to skip
# it because that would result in incorrect line numbers in any errors
# reported by rustc, instead we just comment out the shebang but leave
# it on the same line as `fn main() {`.
printf "fn main() {//%s\n}" "$(cat $0)" | rustc - -o ${OUT}
# Save rustc's exit code so we can compare against it later.
RUSTC_STATUS=$?
fi
# Check if we compiled the script OK, or exit bubbling up the return code.
if test "${RUSTC_STATUS}" -ne 0; then
exit ${RUSTC_STATUS}
fi
# Save the MD5 of the current version of the script so we can compare
# against it next time.
printf "%s" ${MD5} > ${OUT}.md5
fi
# Execute the compiled output. This also ends execution of the shell script,
# as it actually replaces its process with ours; see exec(3) for more on this.
exec ${OUT} $#
# At this point, it's OK to write raw rust code as the shell interpreter
# never gets this far. But we're actually still in the rust comment we opened
# on line 2, so close that: */

Bash does not print any error msg upon non-existing commands starting with dot

This is really just out of curiosity.
A typo made me notice that in Bash, the following:
$ .anything
does not print any error ("anything" not to be interpreted literally, it can really be anything, and no space after the dot).
I am curious about how this is interpreted in bash.
Note that echo $? after such command returns 127. This usually means "command not found". It does make sense in this case, however I find it odd that no error message is printed.
Why would $ anything actually print bash:anything: command not found... (assuming that no anything cmd is in the PATH), while $ .anything slips through silently?
System: Fedora Core 22
Bash version: GNU bash, version 4.3.39(1)-release (x86_64-redhat-linux-gnu)
EDIT:
Some comments below indicated the problem as non-reproducible at first.
The answer of #hek2mgl below summarises the many contributions to this issue, which was eventually found (by #n.m.) as reproducible in FC22 and submitted as a bug report in https://bugzilla.redhat.com/show_bug.cgi?id=1292531
bash supports a handler for situations when a command can't be found. You can define the following function:
function command_not_found_handle() {
command=$1
# do something
}
Using that function it is possible to suppress the error message. Search for that function in your bash startup files.
Another way to find that out is to unset the function. Like this:
$ unset -f command_not_found_handle
$ .anything # Should display the error message
After some research, #n.m. found out that the described behaviour is by intention. FC22 implements command_not_found_handle and calls the program /etc/libexec/pk-command-not-found. This program is part of the PackageKit project and will try to suggest installable packages if you type a command name that can't be found.
In it's main() function the program explicitly checks if the command name starts with a dot and silently returns in that case. This behaviour was introduced in this commit:
https://github.com/hughsie/PackageKit/commit/0e85001b
as a response to this bug report:
https://bugzilla.redhat.com/show_bug.cgi?id=1151185
IMHO this behaviour is questionable. At least other distros are not doing so. But now you know that the behaviour is 100% reproducible and you may follow up on that bug report.

Copy files to the target directory after build

Let's assume I have a game with the following directory structure:
/src
/resources
Cargo.toml
I would like cargo build to copy the files in the resources directory and paste them in the same directory as the executable file.
I know it is possible to do this using a custom build script, but this seems to be a common case that deserves special treatment. So the question is: does cargo provide a standard way of copying files to the target directory (using just Cargo.toml)?
No, it doesn't.
You can move files around with build scripts, but these are run before your crate is built because their sole purpose is to prepare the environment (e.g. compile C libraries and shims).
If you think this is an important feature, you can open a feature request in Cargo issue tracker.
Alternatively, you can write a makefile or a shell script which would forward all arguments to cargo and then copy the directory manually:
#!/bin/bash
DIR="$(dirname "$0")"
if cargo "$#"; then
[ -d "$DIR/target/debug" ] && cp -r "$DIR/resources" "$DIR/target/debug/resources"
[ -d "$DIR/target/release" ] && cp -r "$DIR/resources" "$DIR/target/release/resources"
fi
Now you can run cargo like
% ./make.sh build
I can't solve this for crates (as the accepted answer says) but for a "single" binary that needed a file to run correctly, this works for me.
use std::env;
use std::path::Path;
use std::path::PathBuf;
fn main() {
println!("cargo:rerun-if-changed=config.json");
println!("cargo:warning=Hello from build.rs");
println!("cargo:warning=CWD is {:?}", env::current_dir().unwrap());
println!("cargo:warning=OUT_DIR is {:?}", env::var("OUT_DIR").unwrap());
println!("cargo:warning=CARGO_MANIFEST_DIR is {:?}", env::var("CARGO_MANIFEST_DIR").unwrap());
println!("cargo:warning=PROFILE is {:?}", env::var("PROFILE").unwrap());
let output_path = get_output_path();
println!("cargo:warning=Calculated build path: {}", output_path.to_str().unwrap());
let input_path = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).join("config.json");
let output_path = Path::new(&output_path).join("config.json");
let res = std::fs::copy(input_path, output_path);
println!("cargo:warning={:#?}",res)
}
fn get_output_path() -> PathBuf {
//<root or manifest path>/target/<profile>/
let manifest_dir_string = env::var("CARGO_MANIFEST_DIR").unwrap();
let build_type = env::var("PROFILE").unwrap();
let path = Path::new(&manifest_dir_string).join("target").join(build_type);
return PathBuf::from(path);
}
This is a mess and I'm very new to Rust. Improvements welcome.

Only show first screenful of compile errors in Rust when building with Cargo?

Is there a way to get rustc to only output the first few errors when compiling with Cargo, or even better, to print the oldest errors last? It seems the default threshold for aborting the compile is set quite high:
error: aborting due to 25 previous errors
I don't have the patience to scroll through 6-10 pages of text to find the first error.
Normally I would handle this by compiling inside my editor (vim), but the vim configuration that ships with rust doesn't seem to be setting errorformat properly.
Piping to a pager is also failing for some reason:
cargo test | less
cargo test writes errors to stderr, so you have to redirect stderr to stdout like this:
cargo test --color always 2>&1 | less -r

Resources