Implementing IntoIterator on a reference to struct that wraps a HashMap
Asked Answered
H

1

5

I'm attempting to build a sort of HTTP web server as a learning exercise and I'm having trouble trying to make one of my types iterable using a for loop (by implementing IntoIterator).

So far

I've created a data structure whereby I have a Response object (meant to model a HTTP response), and that contains a Headers object (meant to model the HTTP response headers), and that contains a HashMap<String, String>, representing the actual header values:

use std::collections::HashMap;

struct Headers {
    headers: HashMap<String, String>,
}

impl Headers {
    pub fn new(headers: HashMap<String, String>) -> Self {
        Self { headers }
    }
}

struct Response {
    headers: Headers,
}

impl Response {
    pub fn new(headers: Headers) -> Self {
        Self { headers }
    }

    pub fn headers(&self) -> &Headers {
        &self.headers
    }
}

Note: The headers method returns an immutable reference because I just want to read the values. I ideally don't want to copy/clone the headers just so that they can be read.

Aim

What I want to be able to do is something like this:

fn main() {
    // just creating an example here
    let mut headers = HashMap::new();
    headers.insert("foo".to_string(), "one".to_string());
    headers.insert("bar".to_string(), "two".to_string());
    let response = Response::new(Headers::new(headers));

    // this is what I'm aiming for:
    for (key, value) in response.headers() {
        println!("{}: {}", key, value);
    }
}

What I've tried

I've tried a whole bunch of things to get this working, but I've so far failed. The closest I've come so far is to implement IntoIterator for &Headers and then call into_iter() on the hash map:

impl IntoIterator for &Headers {
    type Item = (String, String);
    type IntoIter = IntoIter<String, String>;

    fn into_iter(self) -> Self::IntoIter {
        self.headers.into_iter()
    }
}

However, this results in an error:

cannot move out of self.headers which is behind a shared reference

I've tried searching around and found numerous StackOverflow questions that are similar, but none of them seem to answer my exact question.

Thanks.

Handedness answered 23/8, 2022 at 18:19 Comment(0)
M
7

What you're doing right now is delegating to the IntoIterator implementation of HashMap<String, String>. But you need to delegate to the IntoIterator implementation of &HashMap<String, String> because you can't get at an owned version of self.headers when self is a reference.

This is an easy fix though, thankfully:

// Just giving this type a concise name so we can reference it easyily later
type HeaderMap = HashMap<String, String>;


impl<'h> IntoIterator for &'h Headers {
    // Here we just tell Rust to use the types we're delegating to.
    // This is just (&'h String, &'h String)
    type Item = <&'h HeaderMap as IntoIterator>::Item;
    type IntoIter = <&'h HeaderMap as IntoIterator>::IntoIter;

    fn into_iter(self) -> Self::IntoIter {
        // Now just call `into_iter` on the correct thing
        (&self.headers).into_iter()
        // self.headers.iter() would work just as well here
    }
}
Motel answered 23/8, 2022 at 18:31 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.