锘??xml version="1.0" encoding="utf-8" standalone="yes"?> (Jin Qing's Column, Oct., 2022) cursive::Cursive can store any user data: Example: (Jin Qing's Column, Apr., 2022) This code compiles error: Fix: Also see: https://github.com/rust-lang/rust/issues/58639 Sometimes, Rust needs a type annotation in closure. (Jin Qing's Column, Mar., 2022) rustup-init.exe may fail if some anti-virus software is running with realtime protection. The error message is like this after many retries: See: https://github.com/rust-lang/rustup/issues/1912 And it is not possible to stop the anti-virus for a corporate laptop. There is a way to workaround this. Here are the steps:
Rerun (Jin Qing's Column, Mar., 2022) It is best to let the function take a closure trait as the parameter instead of a function pointer. compiles error:
https://doc.rust-lang.org/book/ch19-05-advanced-functions-and-closures.html
Function pointers implement all three of the closure traits (Fn, FnMut, and FnOnce), so you can always pass a function pointer as an argument for a function that expects a closure. It’s best to write functions using a generic type and one of the closure traits so your functions can accept either functions or closures. (Jin Qing's Column, Feb., 2022) parking_lot has an experimental feature: deadlock_detection. See: https://amanieu.github.io/parking_lot/parking_lot/deadlock/index.html The output is like this: But the deadlock detection thread can not be changed to a tokio task,
because if deadlock happens, all tasks may be blocked, including the deadlock detection,
causing no deadlock error output. In the following example, if the number of the deadlock tasks is larger than the thread number of tokio runtime,
all tasks will be blocked. The output is: If no deadlock, or the number of deadlock tasks is small, the output should be: (Jin Qing's Column, Jan., 2022) Tracing is Rust log crate: https://github.com/tokio-rs/tracing This example code outputs log to stdout and a log file, using a log filter config file,
which can be automatically reloaded on change.
https://gitee.com/jinq0123/tracing-example
Add these dependencies to Cargo.toml: log_filter.txt is the configure file for log.
The change of this file will trigger the reload. The log filter file must exist, otherwise it will be error: The content of log_filter.txt is a single line of filter string.
A filter string consists of one or more comma-separated directives.
The directive syntax is similar to RUST_LOG env val of env_logger’s. See: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/struct.EnvFilter.html Note: span filter directive will keep effective until it exits the span.
]]>
trait MyCallback: FnMut(&u32) -> () { }
impl<F: FnMut(&u32) -> ()> MyCallback for F { }
fn process_data(mut f: impl MyCallback) -> () {
f(&0)
}
fn process_data_2(mut f: impl FnMut(&u32) -> ()) -> () {
f(&0)
}
fn main() {
// Doesn't compile
process_data(|_| ());
// Compiles
process_data_2(|_| ());
}
expected type `for<'r> FnMut<(&'r u32,)>`
found type `FnMut<(&u32,)>`
process_data(|_: &_| ());
]]>
error: could not rename component file from 'C:\.../rust/html' to 'C:\.../rust/html'
rustup-init
until it begins to retry renamingrustup
will be installed before the failurerustup update
to install all.rustup-init
again to setup others.rustup update
will report that rustc was not found:
C:\Users\jinqing01>rustup update
info: syncing channel updates for 'stable-x86_64-pc-windows-msvc'
...
info: removing previous version of component 'rustc'
warning: during uninstall component rustc was not found
...
info: installing component 'rustc'
...
info: checking for self-updates
stable-x86_64-pc-windows-msvc updated - rustc 1.59.0 (9d1b2106e 2022-02-23) (from (rustc does not exist))
info: cleaning up downloads & tmp directories
rustup-init
to setup the toolchain:
C:\Users\jinqing01>cargo --version
error: no override and no default toolchain set
C:\Users\jinqing01>d:
D:\>cd tool
D:\Tool>rustup-init.exe
Welcome to Rust!
This will download and install the official compiler for the Rust
programming language, and its package manager, Cargo.
...
Rust is installed now. Great!
Press the Enter key to continue.
D:\Tool>cargo --version
cargo 1.59.0 (49d8809dc 2022-02-10)
]]>
fn foo(f: fn()) {
f()
}
fn main() {
foo(|| println!("hello"));
let a = 123;
foo(move || println!("{}", a))
}
error[E0308]: mismatched types
--> src/main.rs:9:9
|
9 | foo(move || println!("{}", a))
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure
|
= note: expected fn pointer `fn()`
found closure `[closure@src/main.rs:9:9: 9:34]`
note: closures can only be coerced to `fn` types if they do not capture any variables
--> src/main.rs:9:32
|
9 | foo(move || println!("{}", a))
| ^ `a` captured here
For more information about this error, try `rustc --explain E0308`.
]]>
use std::thread;
use std::time::Duration;
use parking_lot::deadlock;
// Create a background thread which checks for deadlocks every 10s
thread::spawn(move || {
loop {
thread::sleep(Duration::from_secs(10));
let deadlocks = deadlock::check_deadlock();
if deadlocks.is_empty() {
continue;
}
println!("{} deadlocks detected", deadlocks.len());
for (i, threads) in deadlocks.iter().enumerate() {
println!("Deadlock #{}", i);
for t in threads {
println!("Thread Id {:#?}", t.thread_id());
println!("{:#?}", t.backtrace());
}
}
}
});
1 deadlocks detected
Deadlock #0
Thread Id 16072
0: 0x7ff985cb659d - backtrace::backtrace::dbghelp::trace
at d:\Users\jinqing\.cargo\registry\src\github.com-1ecc6299db9ec823\backtrace-0.3.63\src\backtrace\dbghelp.rs:98
...
13: 0x7ff985ae92f3 - lock_api::rwlock::RwLock<parking_lot::raw_rwlock::RawRwLock,cgc::scene_template::SceneTemplate>::read<parking_lot::raw_rwlock::RawRwLock,cgc::scene_template::SceneTemplate>
at d:\Users\jinqing\.cargo\registry\src\github.com-1ecc6299db9ec823\lock_api-0.4.6\src\rwlock.rs:448
14: 0x7ff985aeadf3 - cgc::scene::SceneData::check_body_collide
at E:\gitlab\yserver\gc\src\scene.rs:99
...
81: 0x7ff9f29b7034 - BaseThreadInitThunk
82: 0x7ff9f2b02651 - RtlUserThreadStart
use std::{thread, time::Duration};
use parking_lot::RwLock;
use tokio::time;
#[tokio::main]
async fn main() {
tokio::spawn(async move {
for i in 0..999999 {
println!("{}", i);
time::sleep(Duration::from_secs(1)).await;
}
});
const MAX: i32 = 100;
for _ in 0..MAX {
tokio::spawn(async move {
{
// DEADLOCK!
let a = RwLock::new(());
let _g1 = a.write();
let _g2 = a.write();
}
time::sleep(Duration::from_secs(9999)).await;
});
}
println!("Hello, world!");
thread::sleep(Duration::from_secs(10));
}
0
Hello, world!
_
0
Hello, world!
1
2
3
...
]]>Dependencies
[dependencies]
anyhow = "1.0.52"
hotwatch = "0.4.6"
tracing = "0.1.29"
tracing-appender = "0.2.0"
tracing-subscriber = { version = "0.3.5", features = [ "env-filter", "json" ] }
main.rs
mod log;
use anyhow::Result;
use std::{thread, time::Duration};
use tracing::info;
fn main() -> Result<()> {
let _guard = log::init("./log", "example.log", "config/log_filter.txt")?;
for i in 0..999 {
info!(i, "Hello, world!");
thread::sleep(Duration::from_secs(1));
}
Ok(())
}
log.rs
//! Init log.
//!
use anyhow::{anyhow, Context as _, Result};
use hotwatch::{Event, Hotwatch};
use std::fs;
use std::path::Path;
use tracing::{debug, warn, Subscriber};
use tracing_appender::{non_blocking::WorkerGuard, rolling};
use tracing_subscriber::{fmt, layer::SubscriberExt, reload::Handle, EnvFilter};
/// Inits log.
/// Returns a WorkerGuard to ensure buffered logs are flushed,
/// and a Hotwatch to watch the log filter file.
pub fn init(
directory: impl AsRef<Path>,
file_name_prefix: impl AsRef<Path>,
log_filter_file: impl AsRef<Path>,
) -> Result<(WorkerGuard, Hotwatch)> {
let file_appender = rolling::daily(directory, file_name_prefix);
let (non_blocking, worker_guard) = tracing_appender::non_blocking(file_appender);
let file_layer = fmt::Layer::default()
.with_writer(non_blocking)
.json()
.flatten_event(true)
.with_ansi(false);
let builder = tracing_subscriber::fmt()
.pretty()
.with_env_filter(EnvFilter::from_default_env())
.with_filter_reloading();
let handle = builder.reload_handle();
let subscriber = builder.finish();
let subscriber = subscriber.with(file_layer);
tracing::subscriber::set_global_default(subscriber).context("set global default subscriber")?;
reload_filter(handle.clone(), log_filter_file.as_ref());
let log_filter_path_buf = log_filter_file.as_ref().to_path_buf();
let mut hotwatch = Hotwatch::new().context("hotwatch failed to initialize!")?;
hotwatch
.watch(log_filter_file.as_ref(), move |event: Event| {
debug!("log filter file event: {:?}", event);
if let Event::Write(_) = event {
reload_filter(handle.clone(), log_filter_path_buf.clone());
}
})
.with_context(|| format!("failed to watch file: {:?}", log_filter_file.as_ref()))?;
Ok((worker_guard, hotwatch))
}
fn reload_filter<S: Subscriber + 'static>(
handle: Handle<EnvFilter, S>,
log_filter_file: impl AsRef<Path>,
) {
let res = try_reload_filter(handle, log_filter_file);
match res {
Ok(_) => debug!("reload log filter OK"),
Err(e) => warn!("reload log filter error: {:?}", e),
}
}
fn try_reload_filter<S: Subscriber + 'static>(
handle: Handle<EnvFilter, S>,
log_filter_file: impl AsRef<Path>,
) -> Result<()> {
let contents = fs::read_to_string(log_filter_file.as_ref()).with_context(|| {
format!(
"something went wrong reading the file: {:?}",
log_filter_file.as_ref()
)
})?;
let contents = contents.trim();
debug!("reload log filter: {:?}", contents);
let new_filter = contents
.parse::<EnvFilter>()
.map_err(|e| anyhow!(e))
.context("failed to parse env filter")?;
handle.reload(new_filter).context("handle reload error")
}
log_filter.txt
Error: failed to watch file: "config/log_filter.txt"
Caused by:
緋葷粺鎵句笉鍒版寚瀹氱殑璺緞銆?(os error 3)
target[span{field=value}]=level
Example
tracing_example
enables logs that:
tracing_example*
info
will enable logs that:
info
tracing_ex=info
enables logs that:
info,tracing_ex=debug
enables logs that:
[foo]=trace
enables logs that:
foo
[span_b{name=\"bob\"}]
enables logs that:
]]>
By default a Box<dyn Trait>
doesn't implement the trait of the object it contains. This means that trying to construct PeopleZoo<Box<dyn Person>>
won't work out of the box and will give a type error.
Because of this, it's good practice to give a default implementation of your trait for it's boxed counterpart. This can be done by calling as_ref
or as_mut
on the Box
and calling the references relevant method.
For just a small bit of effort you can help a bunch of people that may consume your struct.
struct PeopleZoo<P: Person> {
people: Vec<P>,
}
(閲戝簡鐨勪笓鏍?2021.11)
slog 涓嶆槸绔嬪嵆鍐欑洏錛屾渶鍚庣殑鍑犺鏃ュ織浼?xì)涓㈠?/p>
slog 涓嶆敮鎸佽繍琛屼腑鍔ㄦ佹洿鏀規(guī)棩蹇楃瓑綰?/p>
tracing with_filter_reloading()
slog 涓嶆敮鎸佷笉鍚屽寘璁劇疆涓嶅悓鏃ュ織絳夌駭
鍙戠幇涓嬈?slog panic: panicked at 'slog::Fuse Drain: Fatal(Custom { kind: BrokenPipe, error: "The logger thread terminated" })'
鎼滃埌鐩稿悓閿欒錛?a target='_blank' class='url'>https://github.com/mimblewimble/grin/issues/946
slog 涓婚〉10鏈堟坊鍔犳帹鑽愪嬌鐢?tracing
slog 鎸夌嚎紼嬩繚瀛樹笂涓嬫枃錛宼racing 鎸夊崗紼嬩繚瀛樹笂涓嬫枃
tracing github star 1.9K > slog 1.2K
tracing crates 涓嬭澆 21M > sloggers 0.4M, slog-scope 2.6M, slog-stdlog 1.9M
tracing vs slog: https://www.reddit.com/r/rust/comments/kdo29n/slog_vs_tracing_which_one_do_you_prefer/
Rust compiler 2020騫村凡鏀圭敤 tracing錛?a target='_blank' class='url'>https://github.com/rust-lang/rust/pull/74726
tracing 鏇村ソ鐢?/p>
(Jin Qing's Column, Nov. 18, 2021)
There is no "named function parameters" in Rust,
but there is an idiom for this case.
If a function needs many parameters, we can define a type
with all these parameters as fields, and implement Default
trait for this type.
Then we can input this type as the function parameter,
with some of the fields specified and others using default.
foo(Config {
a: 123,
b: bool,
...Default::default(),
});
(Jin Qing's Column, Nov. 18, 2021)
Rust traits are different from interfaces of C++/Java/Go.
See: https://stevedonovan.github.io/rustifications/2018/09/08/common-rust-traits.html
Rust traits are mechanism for adding behavior to types.
Traits have 2 modes. One is interface as Java.
Another is generic constraint. Generic functions are defined over types that implemented specific traits.
The "complie-time duck typing" in C++ templates is avoided in Rust. Rust will reject a type with quack() method as a Duck type. We must pass a type which implements Duck trait. But in Go, a type with quack() method is sufficient to be used as a Duck interface.
(Jin Qing's Column, Nov. 4, 2021)
My Rust program failed when running in docker:
root@a26b49c91efb:/myapp# ldd libmyapp_py.so
./libmyapp_py.so: /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.29' not found (required by ./libmyapp_py.so)
The problem is because I build in "rust:1.56", and run in "debian:buster-slim" which is quite old. Run dpkg -l libc-bin
shows the libc version is 2.28:
C:\Users\jinqing01>docker run debian:buster-slim dpkg -l libc-bin
Unable to find image 'debian:buster-slim' locally
buster-slim: Pulling from library/debian
b380bbd43752: Already exists
Digest: sha256:544c93597c784cf68dbe492ef35c00de7f4f6a990955c7144a40b20d86a3475f
Status: Downloaded newer image for debian:buster-slim
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-==============-============-============-=================================
ii libc-bin 2.28-10 amd64 GNU C Library: Binaries
Using the latest "debian:bookworm-slim" solved the problem.
Dockerfile:
FROM rust:1.56 as chef
RUN cargo install cargo-chef
WORKDIR /myapp
FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
FROM chef AS builder
COPY --from=planner /myapp/recipe.json recipe.json
# Build dependencies
RUN cargo chef cook --release --recipe-path recipe.json
# Build application
COPY . .
RUN cargo build --release
FROM debian:bookworm-slim AS runtime
WORKDIR /myapp
RUN apt-get update && apt-get install -y python3 python3-pip && rm -rf /var/lib/apt/lists/*
RUN pip3 install protobuf
COPY --from=builder /myapp/target/release/*.so /myapp/
COPY --from=builder /myapp/target/release/myapp /myapp/
COPY --from=builder /myapp/tests /myapp/tests
CMD ["myapp"]
Idioms for Rust libraries are still forming, but if your library needs to report custom errors, then you should probably define your own error type. It’s up to you whether or not to expose its representation (like ErrorKind
) or keep it hidden (like ParseIntError
). Regardless of how you do it, it’s usually good practice to at least provide some information about the error beyond just its String
representation. But certainly, this will vary depending on use cases.
At a minimum, you should probably implement the Error
trait. This will give users of your library some minimum flexibility for composing errors. Implementing the Error
trait also means that users are guaranteed the ability to obtain a string representation of an error (because it requires impls for both fmt::Debug
and fmt::Display
).
Beyond that, it can also be useful to provide implementations of From
on your error types. This allows you (the library author) and your users to compose more detailed errors. For example, csv::Error
provides From
impls for both io::Error
and byteorder::Error
.
Finally, depending on your tastes, you may also want to define a Result
type alias, particularly if your library defines a single error type. This is used in the standard library for io::Result
and fmt::Result
.
From: Error Handling in Rust - Andrew Gallant's Blog (burntsushi.net)