I'm using Rust 1.51 and this minimal crate:
#![no_std]
fn main() {
let a = 2.0.cos();
}
I'm building it with cargo check --target thumbv7em-none-eabihf and the compiler complains with this message: no method named 'cos' found for type '{float}' in the current scope. Same for sin() and log10().
I found https://docs.rust-embedded.org/cortex-m-quickstart/cortex_m_quickstart/ and I would expect the above message for targets thumbv6m-none-eabi or thumbv7em-none-eabi but not for thumbv7em-none-eabihf which has FPU support.
How can I solve this?
In Rust 1.51 (and below) functions like sin, cos, or log10 are not part of the core library (core::) but only the standard library (std::), therefore they are not available.
A practical solution is to use the crate libm which offers typical mathematic functions for no_std-environments.
#![no_std]
fn main() {
let a = libm::cosf(2.0);
}
See:
libm crate: https://crates.io/crates/libm
Discussion to move math functions to core library https://github.com/rust-lang/rfcs/issues/2505
What #phip1611 said is correct, cos is no longer part of the core library and hence not usable in no_std out of the box. You can use libm directly OR via a std friendly wrapper using the num-traits crate with the libm feature enabled. This lets you write no_std code as you would write std code. E.g.
# cargo.toml
num-traits = { version = "0.2", default-features = false, features = ["libm"] }
And then in your code:
#![no_std]
#[allow(unused_imports)]
use num_traits::real::Real;
fn main() {
let a = 2.0.cos();
}
Related
I would like to include a dynamic C library in Rust with FFI.
The library is actually also build with Rust, but exposes a C interface, so that it can be used from other languages, too. When I build the library (type: cdylib) with cargo I get a .dylib on MacOS and a .dll as well as a .dll.lib file on windows. These libraries also get different names, derived from the project name (libmy_lib.dylib on MacOS and my_lib.dll as well as my_lib.dll.lib on Windows).
I would like to reference these files in a cross-platform way. Because currently I have to use
#[link(name = "my_lib.dll", kind = "dylib")]
on windows, whereas on MacOS I need to use
#[link(name = "my_lib", kind = "dylib")]
I have already tried to rename the my_lib.dll.lib to my_lib.lib, but I still get a Linker Error, saying
LINK : fatal error LNK1181: cannot open input file 'my_lib.lib'
How can I reference the files, so that I can use my code for Mac and Windows? If thats only possible with cfg_attr tags I would also accept that. Ideally I would also like to get rid of the .lib file for windows if possible.
You could use crate libloading
Example:
let lib = unsafe {
#[cfg(unix)]
let path = "mylib.so";
#[cfg(windows)]
let path = "mylib.dll";
libloading::Library::new(path).expect("Failed to load library")
};
let func: libloading::Symbol<unsafe extern fn() -> u32> = unsafe {
lib.get(b"my_func").expect("Failed to load function `my_func`")
};
// Can call func later while `lib` in scope
For what it's worth, I found a temporary solution for this now.
I used this pattern:
#[cfg(windows)]
#[link(name = "my_lib.dll", kind = "dylib")]
extern {
// Reference the exported functions
}
#[cfg(unix)]
#[link(name = "my_lib", kind = "dylib")]
extern {
// Reference the exported functions
}
I don't like it that much, because I had to define the very same extern{} block twice, but it works and I could also extend this pattern to for example use #[cfg(target_os = "macos")] if needed...
EDIT: Thanks to #Angelicos Phosphoros I improved the code a bit by using a macro like so:
/// Import native functions with the Rust FFI
macro_rules! import_native_functions {
() => {
// Reference the exported functions
};
}
#[cfg(windows)]
#[link(name = "my_lib.dll", kind = "dylib")]
extern {
import_native_functions!();
}
#[cfg(unix)]
#[link(name = "my_lib", kind = "dylib")]
extern {
import_native_functions!();
}
So we are currently trying to compile some Rust code that we can then link to some C code. To do this we are using Bindgen to generate an FFI, and then we will use it to call some C functions from Rust.
However, we must first have the crate "libc" as a dependency in the Cargo.toml file of the project. The project we are currently working on demands that we use the crate wide !#[no_std] attribute, as we don't want the entire stdlib of Rust. We only need the core. The libc crate says we can "request" it not be linked to the standard library by putting some options in the Cargo.toml file, namely:
[dependencies]
libc = { version = "0.2", default-features = false }
This is all fine and dandy, but when we try to compile we get the following error message.
Ubuntu:~/nautilus/src/rust/example# cargo build
Compiling example v0.0.0 (/home/me/nautilus/src/rust/example)
error[E0152]: duplicate lang item found: `panic_impl`.
--> src/lib.rs:33:1
|
33 | / pub fn nk_rust_panic(_info: &PanicInfo) -> !
34 | | {
35 | | // should call nk_panic here...
36 | | loop { }
37 | | }
| |_^
|
= note: first defined in crate `std`.
error: aborting due to previous error
For more information about this error, try `rustc --explain E0152`.
error: Could not compile `example`.
To learn more, run the command again with --verbose.
E0152 is the following
A lang item was redefined.
Erroneous code example:
```
#![feature(lang_items)]
#[lang = "arc"]
struct Foo; // error: duplicate lang item found: `arc`
```
Lang items are already implemented in the standard library. Unless you are
writing a free-standing application (e.g. a kernel), you do not need to provide
them yourself.
You can build a free-standing crate by adding `#![no_std]` to the crate
attributes:
```
#![no_std]
```
Our lib.rs file has #![no_std] already in it, but it appears that since libc would normally link against stdlib, that maybe Rust thinks that we cannot have a custom panic handler if we are using libc.
The problem relieves itself when we remove libc from the Cargo.toml file, and remove the extern crate lib from lib.rs.
lib.rs
// no stdlib
#![no_std]
// Give us this feature to override?
#![feature(start)]
#![feature(lang_items)]
// avoid buildins - we want it to use our library
#![no_builtins]
// The following cruft is here to handle Rust->OS dependencies
// currently only one: Rust needs to know how to panic
use core::panic::PanicInfo;
extern crate libc;
#[panic_handler]
#[no_mangle]
pub fn nk_rust_panic(_info: &PanicInfo) -> !
{
// should call nk_panic here... (panic handler of OS)
loop { }
}
Cargo.toml
[package]
name = "example" # this is for core-kernel
version = "0.0.0"
[lib]
crate-type = ["staticlib"]
[dependencies]
libc = { version = "0.2", default-features = false }
[build-dependencies]
bindgen = "0.42.2"
[profile.dev]
panic = "abort" # no stack unwind on rust panic
[profile.release]
panic = "abort" # no stuck unwind on rust panic
So, in sum, does using libc as a dependency make us unable to use our own custom panic handler? Are there any wonky compiler flags we can pass directly to rustc to eliminate this problem?
Compiler is 1.32.0-nightly
Libc version is libc v0.2.44
You are running into a bug in Cargo.
Features are a bit subtle. They can be enabled by any transitive dependency, which is expected behaviour. However, they can even be enabled by transitive build dependencies, which is a bug.
Using the below code as an example:
extern crate libc;
#[link(name = "adder")]
extern {
fn double_input(input: libc::c_int) -> libc::c_int;
}
fn main() {
let input = 4;
let output = unsafe { double_input(input) };
println!("{} * 2 = {}", input, output);
}
Should #[link(name = "adder")] include a relative path to the .o / a / .h files? For example, should it be #[link(name = "../adderlib/adder")]? Is there another way to tell the compiler where adder is?
The answer to the first question is YES! If your lib file is libfoo.o, #[link(name = "foo") is enough in your code. There are more details in the official documentation.
It will be relative to the lib file which is located in the current work path and the system lib path. (I cannot find this in any documentation, but I once made it successfully). You can specify a path using rustc -l XX -L XX. Using Cargo with a build script is a better way.
If you need to control how a library is found or linked to your Rust code, you should do so via a build script.
I'm trying to link a Rust program with libsoundio. I'm using Windows and there's a GCC binary download available. I can link it like this if I put it in the same folder as my project:
#[link(name = ":libsoundio-1.1.0/i686/libsoundio.a")]
#[link(name = "ole32")]
extern {
fn soundio_version_string() -> *const c_char;
}
But I really want to specify #[link(name = "libsoundio")] or even #[link(name = "soundio")], and then provide a linker path somewhere else.
Where can I specify that path?
I tried the rustc-link-search suggestion as follows:
#[link(name = "libsoundio")]
#[link(name = "ole32")]
extern {
fn soundio_version_string() -> *const c_char;
}
And in .cargo/config:
[target.i686-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/i686"]
rustc-link-lib = ["libsoundio.a"]
[target.x86_64-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/x86_64"]
rustc-link-lib = ["libsoundio.a"]
But it still only passes "-l" "libsoundio" to gcc and fails with the same ld: cannot find -llibsoundio. Am I missing something really obvious? The docs seem to suggest this should work.
As stated in the documentation for a build script:
All the lines printed to stdout by a build script [... starting] with cargo: is interpreted directly by Cargo [...] rustc-link-search indicates the specified value should be passed to the compiler as a -L flag.
In your Cargo.toml:
[package]
name = "link-example"
version = "0.1.0"
authors = ["An Devloper <an.devloper#example.com>"]
build = "build.rs"
And your build.rs:
fn main() {
println!(r"cargo:rustc-link-search=C:\Rust\linka\libsoundio-1.1.0\i686");
}
Note that your build script can use all the power of Rust and can output different values depending on target platform (e.g. 32- and 64-bit).
Finally, your code:
extern crate libc;
use libc::c_char;
use std::ffi::CStr;
#[link(name = "soundio")]
extern {
fn soundio_version_string() -> *const c_char;
}
fn main() {
let v = unsafe { CStr::from_ptr(soundio_version_string()) };
println!("{:?}", v);
}
The proof is in the pudding:
$ cargo run
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target\debug\linka.exe`
"1.0.3"
Ideally, you will create a soundio-sys package, using the convention for *-sys packages. That simply has a build script that links to the appropriate libraries and exposes the C methods. It will use the Cargo links key to uniquely identify the native library and prevent linking to it multiple times. Other libraries can then include this new crate and not worry about those linking details.
Another possible way is setting the RUSTFLAGS like:
RUSTFLAGS='-L my/lib/location' cargo build # or cargo run
I don't know if this is the most organized and recommended approach, but it worked for my simple project.
I found something that works OK: you can specify links in your Cargo.toml:
[package]
links = "libsoundio"
build = "build.rs"
This specifies that the project links to libsoundio. Now you can specify the search path and library name in the .cargo/config file:
[target.i686-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/i686"]
rustc-link-lib = [":libsoundio.a"]
[target.x86_64-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/x86_64"]
rustc-link-lib = [":libsoundio.a"]
(The : prefix tells GCC to use the actual filename and not to do all its idiotic lib-prepending and extension magic.)
You also need to create an empty build.rs:
fn main() {}
This file is never run, because the values in .cargo/config override its output, but for some reason Cargo still requires it - any time you use links = you have to have build =, even if it isn't used.
Finally in main.rs:
#[link(name = "libsoundio")]
#[link(name = "ole32")]
extern {
fn soundio_version_string() -> *const c_char;
}
I am trying to reproduce a TCP client/server chat from an article. I cannot compile the skeleton for the server which uses the mio crate. Here is my code:
extern crate mio;
use mio::*;
struct WebSocketServer;
impl Handler for WebSocketServer {
type Timeout = usize;
type Message = ();
}
fn main() {
let mut event_loop = EventLoop::new().unwrap();
let mut handler = WebSocketServer;
event_loop.run(&mut handler).unwrap();
}
And here is error message which is absolutely unclear for me:
C:\Users\ZZZ\.cargo\git\checkouts\mio-75006fe295376f74\master\src\sys\windows\udp.rs:177:44: 177:61 error: use of unstab
le library feature 'udp_extras': available through the `net2` crate on crates.io
C:\Users\ZZZ\.cargo\git\checkouts\mio-75006fe295376f74\master\src\sys\windows\udp.rs:177 try!(self.inner().socke
t.socket()).set_broadcast(on)
^~~~~~~~~~~~~~~~~
error: aborting due to previous error
Could not compile `mio`.
And here is Cargo.toml:
[package]
name = "rust-chat"
version = "0.2.0"
authors = ["Alex <zzz#yyy.com>"]
[dependencies.mio]
git = "https://github.com/carllerche/mio"
I tried to add net2 to section dependencies explicitly but that still does not work.
Looks like Windows support of mio (or rust) still not very good. So this code, which does not compiled with rustc 1.3-stable was compiled successfully with rusts 1.5-nightly.