I want to implement a lazy static reqwest::ClientBuilder. NB this is for a utilities module, so I have a feeling it'd be difficult to avoid using a static in this case. I want to construct it once, and then use it for building possibly several reqwest::Clients (for different timeouts, which are set on construction of the client).
So I have this line (using once_cell::sync::Lazy):
static CLIENT_BUILDER: Lazy<Mutex<Option<ClientBuilder>>> = Lazy::new(|| Mutex::new(None));
Then I have this function:
pub fn get_reqwest_client(timeout: u64 = 5_000) -> Result<Client, Box<dyn std::error::Error>> {
let mut client_builder_guard = CLIENT_BUILDER.lock().unwrap();
let client_builder = match client_builder_guard.as_ref() {
Some(client_builder) => client_builder,
None => {
// configuration of builder
let es_path = r#"D:\apps\ElasticSearch\elasticsearch-8.6.2\config"#;
set_var("ES_PATH_CONF", &es_path); // from crate tmp_env
let mut buf = Vec::new();
let cert_path = format!(r#"{es_path}\certs\http_ca.crt"#);
let mut cert_file = File::open(cert_path).expect("problem opening certificate file");
let _ = cert_file.read_to_end(&mut buf);
let cert = reqwest::Certificate::from_pem(&buf).expect("problem generating certificate");
let client_builder = Client::builder()
.add_root_certificate(cert);
// setting the Lazy Mutex
*client_builder_guard = Some(client_builder);
client_builder_guard.as_ref().unwrap()
}
};
...
Then a bit later in that function:
let client = client_builder.as_ref()
.timeout(std::time::Duration::from_millis(timeout))
.build()?;
This gives an error:
error[E0599]: the method `as_ref` exists for reference `&ClientBuilder`, but its trait bounds were not satisfied
--> D:\My documents\software projects\EclipseWorkspace\doc_indexer\src\core\utilities\src\lib.rs:154:35
|
154 | let client = client_builder.as_ref()
| ^^^^^^ method cannot be called on `&ClientBuilder` due to unsatisfied trait bounds
|
::: D:\apps\rust\rust_1.70.0\.cargo\registry\src\index.crates.io-6f17d22bba15001f\reqwest-0.11.20\src\blocking\client.rs:69:1
|
69 | pub struct ClientBuilder {
| ------------------------ doesn't satisfy `reqwest::blocking::ClientBuilder: AsRef<_>`
|
= note: the following trait bounds were not satisfied:
`reqwest::blocking::ClientBuilder: AsRef<_>`
which is required by `&reqwest::blocking::ClientBuilder: AsRef<_>`
I've also tried *client_builder and client_builder.clone(); and even client_builder_guard.as_ref().unwrap(). Nothing seems to work.
Why is the Copy trait involved in this and is there a solution? I've reread the compiler messaging and don't understand (I found this answer for example, but on the face of it it doesn't seem to be applicable).
Edit in response to comment
If I just omit as_ref() in the above (which I had tried) I get:
error[E0507]: cannot move out of `*client_builder` which is behind a shared reference
--> D:\My documents\software projects\EclipseWorkspace\doc_indexer\src\core\utilities\src\lib.rs:153:20
|
153 | let client = client_builder
| __________________________^
154 | | .timeout(std::time::Duration::from_millis(timeout))
| |___________________________________________________________________^ move occurs because `*client_builder` has type `reqwest::blocking::ClientBuilder`, which does not implement the `Copy` trait
The answer to this question may help others, not just with
reqwest::ClientBuilderbut possibly also with other "builder" type patterns.The explanation for the problem pointed out by user4815162342 is that
ClientBuilder.build's signature ispub fn build(self) -> Result<Client>. It thus consumes the builder, meaning that a new builder is needed for each client. The problems which were caused by trying to create a reusable builder caused the difficult-to-understand compiler messages.... unfortunately
set_timeoutisn't one of the optional chainable methods available forreqwest::Client, so I opted for a static timeout-to-client map and had hoped that having a single builder might speed up the creation of possible multiple clients. This is not possible.