-1

I am using the proptest crate to run some property tests in a no_std environment. proptest's default test_runner::TestRunner::run() implementation takes some input (as a Strategy or ValueTree object defined in proptest) and a function closure as parameters so you can run however many tests you want with the values in the value tree or strategy.

I need to test a function call that takes as an argument a mutably borrowed struct. This won't compile:

error[E0596]: cannot borrow `*<obj>` as mutable, as it is a captured variable in a 'Fn' closure

obj is the struct passed to test_runner via a function call one level up as a mutably borrowed object.

Digging into the source code for proptest, it is clear the compilation error arises due to the function signature for test_runner::TestRunner::run() :

pub fn run<S: Strategy>(
    &mut self,
    strategy: &S,
    test: impl Fn(S::Value) -> TestCaseResult,
) -> TestRunResult<S>

The parameter would need to be FnMut so that captured variables can be mutable in the function call. My question is: is there an idomatic rust way of doing this?

Here is the function passed from a conditionally compiled run func:

fn test_mod(runner: &mut TestRunner, obj: &mut MyObjStruct) -> Result<(), TestError(u32)>>{
    runner.run(&(0x400_0000u32..0xe00_0000u32), |addr| {
        if mod::test::test_page_map(addr, obj).is_ok() {
            Ok(())
        } else {
            Err(TestCaseError::fail(Reason::from("Failed to map address")))
        }
    })
}

mod::test::test_page_map takes obj, uses relevant metadata in the struct, updates that data accordingly, and maps a page to the input address. It returns a result based on whether or not the map succeeded. It is worth noting that I am not able to clone the object.

Does anyone see a way around this problem that is in line with 'the Rust way'? Is there some mechanism available in no_std land like Cell or something that I can wrap the mutable object with so it has interior mutability but can be passed as a captured variable to a 'Fn' closure? I am very new to Rust so unsure how this sort of thing is usually handled.

Unapiedra
  • 15,037
  • 12
  • 64
  • 93
  • *Is there some mechanism available in no_std land like cell* — do you mean like [`core::cell::Cell`](https://doc.rust-lang.org/core/cell/struct.Cell.html)? – Shepmaster Jul 31 '19 at 20:08
  • Welcome to Stack Overflow! It's hard to answer your question because it doesn't include a [MRE]. We can't tell what crates (and their versions), types, traits, fields, etc. are present in the code. It would make it easier for us to help you if you try to reproduce your error on the [Rust Playground](https://play.rust-lang.org) if possible, otherwise in a brand new Cargo project, then [edit] your question to include the additional info. There are [Rust-specific MRE tips](//stackoverflow.com/tags/rust/info) you can use to reduce your original code for posting here. Thanks! – Shepmaster Jul 31 '19 at 20:08
  • Yea, would that work here to get around the function signature issue? – rusty_prawn Jul 31 '19 at 20:09
  • We cannot tell you that because of my other comment. Without a [MRE], anything we attempt is merely a guess. I would believe that you don't need `no_std` to reproduce your problem whatsoever. I'd also believe that you could write 30-50 lines of code **total** in a brand new Cargo project that only uses proptest to reproduce the problem you are having. – Shepmaster Jul 31 '19 at 20:14
  • Sorry about that, it is a very large project with some restrictive IP issues that prevent me from posting large amounts of the code. I'll read your tips and try to narrow down a better more succinct code chunk if I am able. Generally i am using nightly-7-14-2019, and the most recent versions of all crates although there are only about 3 of them & they are not involved in what I am trying to test. – rusty_prawn Jul 31 '19 at 20:21
  • 1
    To be clear, we **do not want your code**. Period. Especially "large amounts". There should be zero IP issues, because you take your code locally, minimize it, minimize it, minimize it, then give it some new names at the end (then minimize some more). That's what you post. It generally also cleans up your question because the fact that you are (a) using ARM (b) using `no_std` (c) using proptest (d) using nightly (e) using custom test frameworks simply isn't likely to be important to the root problem. – Shepmaster Jul 31 '19 at 20:25
  • I do not yet have the experience to determine on my own whether or not a)-e) might be relevant to the problem, so creating an MRE that excludes those things did not seem entirely possible. But I appreciate your feedback on how to post to stackoverflow so that people can more easily answer. – rusty_prawn Jul 31 '19 at 20:51
  • no real experience is required, that’s the best part! It’s a fairly mechanical set of transformations: delete this argument, inline that function, hard code this value, etc. Everyone is capable of deleting code. The tips I link to mostly serve as a checklist to remind people what is available to minimize. – Shepmaster Jul 31 '19 at 21:05

1 Answers1

0

Found an answer that works, although there are likely others. What I found works is to use core::cell::RefCell::new(obj) to wrap the object. Then pass the RefCell-wrapped object to the function as a mutable borrow, by calling the borrow_mut() method provided by RefCell