预期行为:
在 vscode 中单击“运行 doctest”应该从 doctest 片段执行一项测试。
终端输出应该说(“1 通过;”或“1 失败;”)和“1 被过滤掉;”。
实际行为:
在 vscode 中点击“Run doctest”会执行 0 个测试,显示有 2 个被过滤掉了。
终端输出:
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out; finished in 0.00s
源代码:
我行为不端的箱子:https ://github.com/darrow-olykos/rusty-gists/blob/main/crates/my_cache/src/lib.rs
表现板条箱(这不是问题):https ://github.com/darrow-olykos/rusty-gists/blob/main/crates/math/src/lib.rs
我的机器:
macOS 12
rustc 1.57.0
rust analyzer v0.3.954
我为缩小问题范围所做的工作:
- 在终端中运行“相同”命令演示了预期的行为。终端输出显示
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.40s
当我运行时cargo test --doc --package my_cache -- "Cacher<T>::new" --nocapture
,这正是我单击“运行 Doctest”时终端所说的运行。 - 在同一个 repo 中的另一个 crate(称为“math”)中单击“Run Doctest”,展示了预期的行为。
看看我的行为不端的箱子和我的工作箱子之间的区别:
A. 这个行为不端的 crate 的 doctest 位于 where-impl
因为其他 crate 的 doctest 位于文件的根级别。
B. 这个行为不端的 crate 的 doctest 适用于接受closure
类型的通用结构。
C. 执行cargo test
fromcrates/my_cache
演示了预期的行为,具有以下终端输出:
// ... some output omitted
Doc-tests my_cache
running 2 tests
test src/lib.rs - Cacher<T>::new (line 26) ... ok
test src/lib.rs - Cacher<T>::value (line 42) ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.44s
同样,这里是源代码:
行为不端的箱子:https ://github.com/darrow-olykos/rusty-gists/blob/main/crates/my_cache/src/lib.rs
行为板条箱:https ://github.com/darrow-olykos/rusty-gists/blob/main/crates/math/src/lib.rs
也许?值得注意的细节:
- 我已经按照https://github.com/rust-analyzer/rust-analyzer的结构对我的 git 仓库进行了建模,这样我就使用了 cargo
workspaces
并且crates/*
是成员。我有一个根cargo.toml
,可以使用类似的东西指定本地依赖my_cache = {path = "crates/my_cache}
项,但是我想不出这是一个促成因素的原因,因为我已经证明我的math
crate 可以在这种结构中,而不会混淆vscode并过滤掉其中的 doctests意想不到的板条箱。
我的怀疑?:
- 发生了一些事情,导致 doctest 在不应该被过滤掉时被过滤掉。
- 也许当我单击时声称正在执行
Run Doctest
的命令不是正在执行的 ACTUAL 命令。 - 也许(错误?)与闭包类型有关。我忘记了我在哪里读到的,但我隐约记得 Rust 闭包类型是“未命名”的,这使得引用它们变得很奇怪。不幸的是,我找不到我正在查看的详细介绍了此内容的资源。(它可能是涵盖 Rust 编译器以及 Rust 编译器如何在内存中显示数据类型的资源,但我不记得细节(也许阅读本文的人会知道我在这里指的是什么))。
- 也许当我单击时声称正在执行
这是为了长寿而复制到此答案中的行为不端的 crate 源代码,以防我对 github 存储库进行更改:
// Credit to: https://doc.rust-lang.org/book/ch13-01-closures.html
// (I've modified their example to use a HashMap instead of a single value)
use std::collections::HashMap;
/// cacher for calculation with two u64's as input and u64 as output
/// can be generalized more
pub struct Cacher<T>
where
T: FnMut(u64, u64) -> u64,
{
calculation: T,
values: HashMap<String, u64>,
}
impl<T> Cacher<T>
where
T: FnMut(u64, u64) -> u64,
{
/// Returns a Cacher<T> which can cache results of calculations for the provided closure.
///
/// # Arguments
///
/// `T` - Closure that computes produces a value. Value is cached based on args. Cached value is returend on subsequent calls if args are the same.
///
/// # Examples
/// ```rust
/// use my_cache::Cacher;
/// let mut cacher = Cacher::new(|x,y|x+y);
/// ```
pub fn new(calculation: T) -> Self {
let values = HashMap::new();
Cacher {
calculation,
values,
}
}
/// Returns value of calculation `T`. Cached value is returned if unique `n`, `k` pair provided, otherwise calcuation runs and then value is cached.
///
/// # Examples
///
/// ```rust
/// use std::rc::Rc;
/// use std::cell::{RefCell, RefMut};
///
/// use my_cache::Cacher;
///
/// let mut count = Rc::new(RefCell::new(0));
/// let add = |x, y| {
/// let mut count_mut_ref = count.borrow_mut();
/// *count_mut_ref += 1; x + y
/// };
/// let mut cacher = Cacher::new(add);
///
/// assert_eq!(*count.borrow(), 0);
/// assert_eq!(cacher.value(2, 3), 5); // new calculation, count += 1
/// assert_eq!(*count.borrow(), 1);
/// assert_eq!(cacher.value(2, 3), 5); // repeat, use cache
/// assert_eq!(*count.borrow(), 1);
/// assert_eq!(cacher.value(2, 4), 6); // new calculation, count += 1
/// assert_eq!(*count.borrow(), 2);
/// ```
pub fn value(&mut self, n: u64, k: u64) -> u64 {
let key = n.to_string() + &k.to_string();
let cached_result = self.values.get(&key);
if let Some(value) = cached_result {
*value
} else {
let v = (self.calculation)(n, k);
self.values.insert(key, v);
v
}
}
}