How to emulate `super-let` on stable Rust

⚓ Rust    📅 2025-12-23    👤 surdeus    👁️ 6      

surdeus

I want to read an HTTP response body from a cache file; if an error occurs while reading, the data is fetched from the internet.

The cached data and the fetched data have different types: the former is Cache, the latter is Body. To abstract over them, I defined a BodyRef struct that borrows the inner data.

I tried to construct a BodyRef with an if-let-else expression, but the code fails to compile because the lifetime of the fetched data is tied to the else branch.

How should I rewrite this code on stable Rust so the fetched data lives beyond the else branch?

/*
[dependencies]
chrono = { version = "0.4.42", features = ["serde"] }
reqwest = "0.12.28"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.146"
 */

use std::{io, str::FromStr};

use chrono::{DateTime, Utc};
use reqwest::Url;
use serde::{Deserialize, Serialize};
use serde_json::{Map as JsonMap, Value as JsonValue};

struct Body {
    map: JsonMap<String, JsonValue>,
    text: String,
}

struct BodyRef<'a> {
    map: &'a JsonMap<String, JsonValue>,
    text: &'a str,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(try_from = "CacheSerde")]
struct Cache {
    url: Url,
    date: DateTime<Utc>,
    text: String,
    #[serde(skip)] map: JsonMap<String, JsonValue>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct CacheSerde {
    url: Url,
    date: DateTime<Utc>,
    text: String,
}

impl TryFrom<CacheSerde> for Cache {
    type Error = serde_json::Error;

    fn try_from(value: CacheSerde) -> Result<Self, Self::Error> {
        let body: JsonMap<String, JsonValue> = JsonMap::from_str(&value.text)?;
        Ok(Self {
            url: value.url,
            date: value.date,
            text: value.text,
            map: body,
        })
    }
}

fn read_cache() -> io::Result<Cache> { todo!() }

fn write_cache(_: &Cache) -> io::Result<()> { todo!() }

fn send_request() -> io::Result<Body> { todo!() }

fn main() {
    let cache: Option<Cache> = read_cache()
        .inspect_err(|e| {
            eprintln!("INFO: Failed to read cache. Fetching data from the network.\nCache error: {e}");
        })
        .ok();

    let body: BodyRef<'_> = if let Some(cache) = cache.as_ref() {
        BodyRef {
            map: &cache.map,
            text: &cache.text,
        }
    } else {
        // `super let` is experimental
        // super let body = send_request().unwrap();
        let body: Body = send_request().unwrap();

        BodyRef {
            // `body.map` does not live long enough borrowed value does not live long enough [E0597]
            map: &body.map,
            text: &body.text,
        }
    };

    dbg!(body.text);
}

5 posts - 3 participants

Read full topic

🏷️ Rust_feed