从 impl 函数内部更改可变堆栈变量的值?

问题描述 投票:0回答:1

这看起来非常简单。我希望有一个重复的,但我已经搜索过了。

我正在尝试进行一个测试,调用框架的方法会导致在属于该框架的

parse
中的所有
TextDocument
实现对象上调用方法
HashMap<String, Box<dyn TextDocument>>
TextDocument
是一种特质。

所以希望这能让我实现一个像这样的简单的模拟设置:

#[test]
fn each_text_document_is_parsed_in_framework_create_index() -> Result<()>{
    let mut docs_parsed = 0;
    let increment_count = ||{
        docs_parsed += 1
    };
    struct MockTextDocument {
        pub framework: Weak<Framework>,
    }
    impl TextDocument for MockTextDocument {
        fn parse(&self) -> Result<()> {
            increment_count();
            Ok(())
        }
    }

...然后我调用框架的方法来引起这个解析,最后进行测试:

    framework.create_index();
    assert_eq!(docs_parsed, 3);

但这行不通。我从编译器得到这个:

   Compiling populate_index_module v0.1.0 (D:\My documents\software projects\workspace\doc_indexer\src\core\populate_index_module)
error[E0434]: can't capture dynamic environment in a fn item
   --> tests\test_1.rs:158:4
    |
158 |             increment_count();
    |             ^^^^^^^^^^^^^^^
    |
    = help: use the `|| { ... }` closure form instead

我显然是想在这里使用闭包。但显然这不是正确的方法。

顺便说一句,我不能使用可能属于

parsed: bool
的属性(例如
MockTextDocument
),因为由于不可避免的原因,框架是一个
Rc<Framework>
,所以地图中的所有
Box<dyn TextDocument>
值是“共享引用后面”并且无法访问......换句话说,我相信我必须使用外部计数器变量。

在我的特定情况下,一个额外的复杂性是

MockTextDocument
不能即时创建:它只能在函数内部创建(函数本身就是
Framework::new
的参数)。不过,我正在跟进 Vallentin 所说的……

事实上,我认为这是不可能的......所以我想我必须拼凑出另一种不太优雅的方法,比如模拟对象中的

parse
打印“解析...”到
stdout
,然后我可以监控其中的内容。

更多详情(Chayim Friedman)
这就是在测试中创建

Framework
期间创建模拟文档的方式:

fn gather_mock_documents(walker: WalkDir, this: &Weak<Framework>) -> HashMap<String, Box<dyn TextDocument>> {
    let mut map: HashMap<String, Box<dyn TextDocument>> = HashMap::new();
    for i in 0..3 {
        let mock_td = MockTextDocument {
            framework: this.clone(),

            // it seems to me that any closure used here would 
            // have the same problem: no access to the stack variable
        };
        map.insert(format!("mock_doc_{i}"), Box::new(mock_td));
    }
    map
}

let framework = Framework::new("any_dir", String::from("dummy"), 
    &gather_mock_documents);

Framework::new
是这样的:

pub fn new(document_root_path: &str, index_name: String, 
    gather_closure: &dyn Fn(WalkDir, &Weak<Framework>) ->
        HashMap<String, Box<dyn TextDocument>>) -> Rc<Self> {
    let walker = WalkDir::new(document_root_path);
    let hf = Rc::new_cyclic(|this: &std::rc::Weak<Framework>|
        Framework{ 
            index_name,
            root_documents_path_str: String::from(document_root_path),  
            text_doc_map: gather_closure(walker, this),
        }
    );
    hf
}
testing rust
1个回答
0
投票

明白了...感谢 Chayim Friedman 的建议。

struct MockTextDocument {
    pub framework: Weak<HandlingFramework>,
    parse_count: Rc<RefCell<i32>>,
}

impl TextDocument for MockTextDocument {
    fn get_framework(&self) -> &Weak<HandlingFramework>{
        &self.framework
    }

    fn parse(&self) -> Result<()> {
        *self.parse_count.borrow_mut() += 1;
        Ok(())
    }
}

#[test]
fn each_text_document_is_parsed_in_framework_create_index() -> Result<()>{
    let parse_count = Rc::new(RefCell::new(0));
    let gather_mock_documents = |_: WalkDir, this: &Weak<HandlingFramework>| -> HashMap<String, Box<dyn TextDocument>> {
        let mut map: HashMap<String, Box<dyn TextDocument>> = HashMap::new();
        for i in 0..3 {
            let mock_td = MockTextDocument {
                framework: this.clone(),
                parse_count: parse_count.clone(),
            };
            map.insert(format!("mock_doc_{i}"), Box::new(mock_td));
        }
        map
    };

    let framework = HandlingFramework::new("any_dir", String::from("dummy"), 
        &DummyConfiguror, &gather_mock_documents);

    println!("framework type {}", type_name_of_val(&framework));        
    assert_eq!(framework.text_doc_map.len(), 3, "len was {}", framework.text_doc_map.len());
    let _ = framework.create_index();
    assert_eq!(*parse_count.borrow(), 3);
    
    Ok(())      
}
© www.soinside.com 2019 - 2024. All rights reserved.