<p dir="auto">In other languages like C++, Java, C# etc dependency injection can be a big pain; you want your class <code class="notranslate">Foo</code> to use class <code class="notranslate">Bar</code> in production, but class <code class="notranslate">MockBar</code> in tests. This is usually enabled by explicit dependency injection support in class <code class="notranslate">Foo</code> (through ctor/setter/interface injection) and the code that uses <code class="notranslate">Foo</code>s. It's a fairly big hassle and requires a ton of boilerplate code.</p>
<p dir="auto">I'd like to see Rust provide compiler-level support for swapping types when <code class="notranslate">cfg(test)</code>. Something like this:</p>
<div class="highlight highlight-source-rust notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="#[cfg(test)]
mod testmod {
  struct MockBar {
  ...
  }

  #[test]
  #[swap(Bar, MockBar)]
  fn testfunc() { 
    // Foo::new() now creates MockBars instead of Bars internally
    let foo = Foo::new();
    assert_eq!(foo.frobulate(), 5);
  }
}"><pre class="notranslate"><span class="pl-c1">#<span class="pl-kos">[</span>cfg<span class="pl-kos">(</span>test<span class="pl-kos">)</span><span class="pl-kos">]</span></span>
<span class="pl-k">mod</span> testmod <span class="pl-kos">{</span>
  <span class="pl-k">struct</span> <span class="pl-smi">MockBar</span> <span class="pl-kos">{</span>
  ...
  <span class="pl-kos">}</span>

  <span class="pl-c1">#<span class="pl-kos">[</span>test<span class="pl-kos">]</span></span>
  <span class="pl-c1">#<span class="pl-kos">[</span>swap<span class="pl-kos">(</span><span class="pl-v">Bar</span>, <span class="pl-v">MockBar</span><span class="pl-kos">)</span><span class="pl-kos">]</span></span>
  <span class="pl-k">fn</span> <span class="pl-en">testfunc</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> 
    <span class="pl-c">// Foo::new() now creates MockBars instead of Bars internally</span>
    <span class="pl-k">let</span> foo = <span class="pl-smi">Foo</span><span class="pl-kos">::</span><span class="pl-en">new</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-en">assert_eq</span><span class="pl-en">!</span><span class="pl-kos">(</span>foo.frobulate<span class="pl-kos">(</span><span class="pl-kos">)</span>, <span class="pl-c1">5</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
  <span class="pl-kos">}</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">The idea is to swap any uses of <code class="notranslate">Bar</code> with <code class="notranslate">MockBar</code> in all the code that's executed through the annotated test function. The beauty lies in not having to mangle the production code just so we can isolate only the parts we want to test.</p>
<p dir="auto">I'm not sure how difficult it would be to implement this and I don't have this idea fully fleshed out, but the gist should be apparent. It doesn't even have to be an attribute; frankly I don't care <em>how</em> this is implemented as long as I can replace one type with another type that implements the same interface[s] for the purpose of test code <em>and that I don't have to change the production code to do it.</em></p>
<p dir="auto">Any such support should of course only work when <code class="notranslate">cfg(test)</code>. Enabling this in production code would IMO be a bad idea.</p>
<p dir="auto">EDIT: Forgot to mention, being able to swap functions in this way and not just types would make this feature even more awesome.</p>
<div class="highlight highlight-source-rust notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="struct Foo&lt;'a&gt; {
    _s: &amp;'a str
}

struct Bar&lt;H: Handler&gt; {
    handler: H
}

trait Handler {
    fn handle&lt;'a&gt;(&amp;'a self, foo: Foo&lt;'a&gt;);
}

impl&lt;F&gt; Handler for F where F: Fn(Foo) {
    fn handle&lt;'a&gt;(&amp;'a self, foo: Foo&lt;'a&gt;) {
        self(foo)
    }
}

fn main() {
    let foo = Foo { _s: &quot;foo&quot; };
    let bar = Bar { handler: |_f /*: Foo*/| {} }; // uncomment to work
    (bar.handler)(foo);
}"><pre class="notranslate"><span class="pl-k">struct</span> <span class="pl-smi">Foo</span><span class="pl-kos">&lt;</span><span class="pl-c1">'</span><span class="pl-ent">a</span><span class="pl-kos">&gt;</span> <span class="pl-kos">{</span>
    <span class="pl-c1">_s</span><span class="pl-kos">:</span> <span class="pl-c1">&amp;</span><span class="pl-c1">'</span><span class="pl-ent">a</span> <span class="pl-smi">str</span>
<span class="pl-kos">}</span>

<span class="pl-k">struct</span> <span class="pl-smi">Bar</span><span class="pl-kos">&lt;</span><span class="pl-smi">H</span><span class="pl-kos">:</span> <span class="pl-smi">Handler</span><span class="pl-kos">&gt;</span> <span class="pl-kos">{</span>
    <span class="pl-c1">handler</span><span class="pl-kos">:</span> <span class="pl-smi">H</span>
<span class="pl-kos">}</span>

<span class="pl-k">trait</span> <span class="pl-smi">Handler</span> <span class="pl-kos">{</span>
    <span class="pl-k">fn</span> <span class="pl-en">handle</span><span class="pl-kos">&lt;</span><span class="pl-c1">'</span><span class="pl-ent">a</span><span class="pl-kos">&gt;</span><span class="pl-kos">(</span><span class="pl-c1">&amp;</span><span class="pl-c1">'</span><span class="pl-ent">a</span> <span class="pl-smi">self</span><span class="pl-kos">,</span> <span class="pl-s1">foo</span><span class="pl-kos">:</span> <span class="pl-smi">Foo</span><span class="pl-kos">&lt;</span><span class="pl-c1">'</span><span class="pl-ent">a</span><span class="pl-kos">&gt;</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-k">impl</span><span class="pl-kos">&lt;</span><span class="pl-smi">F</span><span class="pl-kos">&gt;</span> <span class="pl-smi">Handler</span> <span class="pl-k">for</span> <span class="pl-smi">F</span> <span class="pl-k">where</span> <span class="pl-smi">F</span><span class="pl-kos">:</span> <span class="pl-smi">Fn</span><span class="pl-kos">(</span><span class="pl-smi">Foo</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-k">fn</span> <span class="pl-en">handle</span><span class="pl-kos">&lt;</span><span class="pl-c1">'</span><span class="pl-ent">a</span><span class="pl-kos">&gt;</span><span class="pl-kos">(</span><span class="pl-c1">&amp;</span><span class="pl-c1">'</span><span class="pl-ent">a</span> <span class="pl-smi">self</span><span class="pl-kos">,</span> <span class="pl-s1">foo</span><span class="pl-kos">:</span> <span class="pl-smi">Foo</span><span class="pl-kos">&lt;</span><span class="pl-c1">'</span><span class="pl-ent">a</span><span class="pl-kos">&gt;</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
        <span class="pl-smi">self</span><span class="pl-kos">(</span>foo<span class="pl-kos">)</span>
    <span class="pl-kos">}</span>
<span class="pl-kos">}</span>

<span class="pl-k">fn</span> <span class="pl-en">main</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-k">let</span> foo = <span class="pl-smi">Foo</span> <span class="pl-kos">{</span> <span class="pl-c1">_s</span><span class="pl-kos">:</span> <span class="pl-s">"foo"</span> <span class="pl-kos">}</span><span class="pl-kos">;</span>
    <span class="pl-k">let</span> bar = <span class="pl-smi">Bar</span> <span class="pl-kos">{</span> <span class="pl-c1">handler</span><span class="pl-kos">:</span> |_f <span class="pl-c">/*: Foo*/</span>| <span class="pl-kos">{</span><span class="pl-kos">}</span> <span class="pl-kos">}</span><span class="pl-kos">;</span> <span class="pl-c">// uncomment to work</span>
    <span class="pl-kos">(</span>bar<span class="pl-kos">.</span><span class="pl-c1">handler</span><span class="pl-kos">)</span><span class="pl-kos">(</span>foo<span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">Gives:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="error: type mismatch: the type `[closure test.rs:8:23: 20:6]` implements the trait `core::ops::Fn&lt;(_, _)&gt;`, but the trait `for&lt;'r,'r,'r&gt; core::ops::Fn&lt;(hyper::server::request::Request
&lt;'r, 'r&gt;, hyper::server::response::Response&lt;'r&gt;)&gt;` is required (expected concrete lifetime, found bound lifetime parameter ) [E0281]"><pre class="notranslate"><code class="notranslate">error: type mismatch: the type `[closure test.rs:8:23: 20:6]` implements the trait `core::ops::Fn&lt;(_, _)&gt;`, but the trait `for&lt;'r,'r,'r&gt; core::ops::Fn&lt;(hyper::server::request::Request
&lt;'r, 'r&gt;, hyper::server::response::Response&lt;'r&gt;)&gt;` is required (expected concrete lifetime, found bound lifetime parameter ) [E0281]
</code></pre></div>
<p dir="auto">Playpen: <a href="http://is.gd/E7sskd" rel="nofollow">http://is.gd/E7sskd</a></p>
<p dir="auto">Couldn't tell if this was <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="40140609" data-permission-text="Title is private" data-url="https://github.com/rust-lang/rust/issues/16473" data-hovercard-type="issue" data-hovercard-url="/rust-lang/rust/issues/16473/hovercard" href="https://github.com/rust-lang/rust/issues/16473">#16473</a>, <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/reem/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/reem">@reem</a> thinks it might be different. Either way, this hurts using hyper's server easily: <code class="notranslate">Server::http(|req, res| {}).listen(8080)</code>.</p>