Rust - Need help making code idiomatic and clean (with lifetimes) This is the first real program I have written in rust. What started as a clean piece of rust code, turned into a lifetime monstrosity when I decided to make my enums and structs use &amp;str instead of String. The algorithm implemented is irrelevant; I just need help making my code more idiomatic and clean. Here is (quite simple) my code:
use std::collections::HashMap;
use std::hash::{Hash, Hasher};

#[derive(Debug, PartialEq, Eq)]
struct BasicBlock&lt;'a&gt; {
    block_id: u32,
    code: Vec&lt;IRCode&lt;'a&gt;&gt;,
    preds: Vec&lt;Box&lt;BasicBlock&lt;'a&gt;&gt;&gt;,
    succs: Vec&lt;Box&lt;BasicBlock&lt;'a&gt;&gt;&gt;,
}

#[derive(Debug, PartialEq, Eq)]
enum IRCode&lt;'a&gt; {
    Basic {
        value: &amp;'a str,
        btype: Type,
        id: u32,
    },
    Quadruple {
        op: Op,
        params: Vec&lt;IRCode&lt;'a&gt;&gt;,
        location: Storage&lt;'a&gt;,
    },
    // and some more...
}

#[derive(Debug, PartialEq, Eq)]
enum Type {
    TVarRef,
    // and some more...
}

#[derive(Debug, PartialEq, Eq)]
enum Storage&lt;'a&gt; {
    Temp { name: &amp;'a str },
}

#[derive(Debug, PartialEq, Eq)]
enum Op {
    Add,
    Sub,
}

impl&lt;'a&gt; Hash for BasicBlock&lt;'a&gt; {
    fn hash&lt;H: Hasher&gt;(&amp;self, state: &amp;mut H) {
        self.block_id.hash(state);
    }
}

#[derive(Debug)]
struct SSA&lt;'a&gt; {
    count: HashMap&lt;&amp;'a str, u32&gt;,
    current_def: HashMap&lt;&amp;'a str, HashMap&lt;Box&lt;&amp;'a BasicBlock&lt;'a&gt;&gt;, Box&lt;IRCode&lt;'a&gt;&gt;&gt;&gt;,
}

impl&lt;'a&gt; SSA&lt;'a&gt; {
    fn new() -&gt; SSA&lt;'a&gt; {
        SSA {
            count: HashMap::new(),
            current_def: HashMap::new(),
        }
    }

    fn new_variable(&amp;mut self, name: &amp;'a str, block: &amp;'a BasicBlock&lt;'a&gt;) -&gt; &amp;Box&lt;IRCode&lt;'a&gt;&gt; {
        // If `name` is not in `count`, insert `(name, 0)`; else increment the count
        *self.count.entry(name).or_insert(0) += 1;
        self.current_def.entry(name).or_insert(HashMap::new());
        self.current_def.get_mut(&amp;name).unwrap().insert(
            Box::new(block),
            Box::new(IRCode::Basic {
                value: name,
                btype: Type::TVarRef,
                id: *self.count.get(&amp;name).unwrap(),
            }),
        );
        self.current_def.get(&amp;name).unwrap().get(&amp;block).unwrap()
    }
}

fn main() {
    // very simple test case
    let mut ssa = SSA::new();
    let bb0 = BasicBlock {
        block_id: 0,
        code: vec![],
        preds: vec![],
        succs: vec![],
    };
    println!(&quot;{:?}&quot;, ssa.new_variable(&quot;foo&quot;, &amp;bb0));
    println!(&quot;{:?}&quot;, ssa);
}

Thanks!
Hello and welcome to the Rust community.
After a single read of your code, I have a couple comments.

your problems with &amp;str as well as keying by block_id would be best remedied by putting interners in SSA. The best interner choice would be string-interner or internment.
in fn new_variable, you have repeated lookups for the same entry of current_def. LLVM will be unable to optimize out repeated accesses to HashMap. This may slow us down by something like 120~200ns per invocation of the function, because one HashMap lookup takes approx. 40~70ns.
you have Vec&lt;Box&lt;...&gt;&gt; and HashMap&lt;Box&lt;...&gt;,Box&lt;...&gt;&gt;. This inclusion of Box within another container immediately raises a flag in the mind of a Rust programmer that the code is not cleaned up. Wrapping in Box does not provide anything over wrapping in Vec, HashMap or any other container. All wraps provide unique ownership with memory indirection. To illustrate with an example, Box&lt;[T]&gt; is almost like Vec in that both are owned dynamic-length slices (indexable memory ranges) except that Box&lt;[T]&gt; does not allow any overprovisioning of memory (Vec can have capacity above length).
the SSA::current_def type expression is basically as long as your average tweet. Break down complex types longer than 30-40 characters into smaller definitions.

We will begin by changing SSA and fn new_variable, with new structure SSADef:
#[derive(Debug)]
struct SSA&lt;'a&gt; {
    content: HashMap&lt;&amp;'a str, SSADef&lt;'a&gt;&gt;,
}

#[derive(Debug)]
struct SSADef&lt;'a&gt; {
    count: u32,
    current_def: HashMap&lt;&amp;'a BasicBlock&lt;'a&gt;, IRCode&lt;'a&gt;&gt;,
}

impl&lt;'a&gt; SSA&lt;'a&gt; {
    fn new() -&gt; SSA&lt;'a&gt; {
        SSA { content: HashMap::new() }
    }

    fn new_variable&lt;'s&gt;(&amp;'s mut self, name: &amp;'a str, block: &amp;'a BasicBlock&lt;'a&gt;) -&gt; &amp;'s IRCode&lt;'a&gt; {
        // If `name` is not in `content`, insert `SSADef::new()`
        let def = self.content.entry(name).or_insert_with(SSADef::new);
        // Increment the count
        def.count += 1;
        def.current_def.entry(block).or_insert(
            IRCode::Basic {
                value: name,
                btype: Type::TVarRef,
                id: def.count,
            }
        )
    }
}

impl&lt;'a&gt; SSADef&lt;'a&gt; {
    fn new() -&gt; SSADef&lt;'a&gt; {
        SSADef {
            count: 0,
            current_def: HashMap::new(),
        }
    }
}

One minor observation: you can derive Default for SSA and SSADef and call default from both news. This will put 0s and empty HashMaps and other default values in the fields of these structures.
We add the StringInterner. I assume the deeper functionality will always have easy access to top level SSA, and consequently easy access to the interner. If not, we can use some other crate such as internment which provides an appropriate API.
Changing the BasicBlocks for interning is left as an exercise for the reader. We arrive at the following code, which has very few 'as left. This is all I can do without knowledge of the algorithm, I believe:
use std::collections::HashMap;
use std::hash::{Hash, Hasher};

use string_interner::{StringInterner, DefaultSymbol as Sym};

#[derive(Debug, PartialEq, Eq)]
struct BasicBlock {
    block_id: u32,
    code: Vec&lt;IRCode&gt;,
    preds: Vec&lt;BasicBlock&gt;,
    succs: Vec&lt;BasicBlock&gt;,
}

#[derive(Debug, PartialEq, Eq)]
enum IRCode {
    Basic {
        value: Sym,
        btype: Type,
        id: u32,
    },
    Quadruple {
        op: Op,
        params: Vec&lt;IRCode&gt;,
        location: Storage,
    },
    // and some more...
}

#[derive(Debug, PartialEq, Eq)]
enum Type {
    TVarRef,
    // and some more...
}

#[derive(Debug, PartialEq, Eq)]
enum Storage {
    Temp { name: Sym },
}

#[derive(Debug, PartialEq, Eq)]
enum Op {
    Add,
    Sub,
}

impl Hash for BasicBlock {
    fn hash&lt;H: Hasher&gt;(&amp;self, state: &amp;mut H) {
        self.block_id.hash(state);
    }
}

#[derive(Debug, Default)]
struct SSA&lt;'a&gt; {
    content: HashMap&lt;Sym, SSADef&lt;'a&gt;&gt;,
    interner: StringInterner,
}

#[derive(Debug, Default)]
struct SSADef&lt;'a&gt; {
    count: u32,
    current_def: HashMap&lt;&amp;'a BasicBlock, IRCode&gt;,
}

impl&lt;'a&gt; SSA&lt;'a&gt; {
    fn new() -&gt; SSA&lt;'a&gt; {
        SSA::default()
    }

    fn new_variable&lt;'s&gt;(&amp;'s mut self, name: &amp;'a str, block: &amp;'a BasicBlock) -&gt; &amp;'s IRCode {
        let name_sym = self.interner.get_or_intern(name);
        // If `name` is not in `content`, insert `SSADef::new()`
        let def = self.content.entry(name_sym).or_insert_with(SSADef::new);
        // Increment the count
        def.count += 1;
        def.current_def.entry(block).or_insert(
            IRCode::Basic {
                value: name_sym,
                btype: Type::TVarRef,
                id: def.count,
            }
        )
    }
}

impl&lt;'a&gt; SSADef&lt;'a&gt; {
    fn new() -&gt; SSADef&lt;'a&gt; {
        SSADef::default()
    }
}

fn main() {
    // very simple test case
    let mut ssa = SSA::new();
    let bb0 = BasicBlock {
        block_id: 0,
        code: vec![],
        preds: vec![],
        succs: vec![],
    };
    println!(&quot;{:?}&quot;, ssa.new_variable(&quot;foo&quot;, &amp;bb0));
    println!(&quot;{:?}&quot;, ssa);
}
```