How to (idiomatically) borrow a str from a String (using the "as" keyword?)

I have a String and want to pass it to this method: soapysdr::Device::new.

A corresponding toy example would be the following function foo:

struct Args { /* … */ }

impl<'a> From<&'a str> for Args {
    fn from(_value: &'a str) -> Self {
        Args { /* …*/ }
    }
}

fn foo<A: Into<Args>>(_args: A) {
    /* …*/
}

Now lets say we have a String named s and we try to pass it with &s:

fn main() {
    let s = String::from("Some string from outside world.");
    //foo(&s); // doesn't work
}

This will fail:

   Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `Args: From<&String>` is not satisfied
  --> src/main.rs:15:9
   |
15 |     foo(&s);
   |     --- ^^ the trait `From<&String>` is not implemented for `Args`
   |     |
   |     required by a bound introduced by this call
   |
   = note: required for `&String` to implement `Into<Args>`
note: required by a bound in `foo`
  --> src/main.rs:9:11
   |
9  | fn foo<A: Into<Args>>(_args: A) {
   |           ^^^^^^^^^^ required by this bound in `foo`
help: consider dereferencing here
   |
15 |     foo(&*s);
   |          +

I actually have several options to fix this:

fn main() {
    let s = String::from("Some string from outside world.");
    //foo(&s); // doesn't work
    foo(&s as &str); // where is this syntax described?
    foo(&*s); // a bit cryptic when reading the code
    foo(<&str>::from(&s)); // I don't really want to allow arbitrary conversions though
}

(Playground)

I'm tempted to use the &s as &str syntax. But what is that syntax actually? I didn't find this use-case of as in the reference or in std (keyword as). Is it correct and/or idiomatic to use it here? Is this something where the documentation isn't complete, or did I just miss something?


Update: I think I found it. It's actually in the reference (as linked above):

as can be used to explicitly perform coercions, […]

So I guess it's a coercion here.

The documentation of std regarding the as keyword doesn't seem to mention this use-case of as though.

I would always opt for String::as_str

6 Likes

Some more options I came up with:

fn main() {
    let s = String::from("Some string from outside world.");
    //foo(&s); // doesn't work
    foo(&s as &str);
    foo(&*s);
    foo(<&str>::from(&s));
    foo(s.as_str());
    foo(AsRef::<str>::as_ref(&s));
    foo(Borrow::<str>::borrow(&s)); // requires `std::borrow::Borrow`
    foo(&s[..]);
    foo(s.deref()); // requires `std::ops::Deref`
    foo({ let x: &str = &s; x});
}

(Playground)

s.as_str() or &*s are what I tend to reach for.

4 Likes

I don't know this for sure, but the as may just be functioning as a type hint, and causing the impl of Borrow<str> to kick in[1]


  1. the reference link there doesn't consider that a coercion aiui? ↩︎

Because Borrow::borrow() isn't a coercion. You are probably thinking about Deref::deref().

1 Like

Borrow is never implicitly invoked.

use std::borrow::Borrow;
use std::ops::Deref;

struct A;
struct B;

impl Borrow<B> for A {
    fn borrow(&self) -> &B {
        &B
    }
}

fn takes_b_ref(_arg: &B) {}

struct C;
struct D;

impl Deref for C {
    type Target = D;
    fn deref(&self) -> &D {
        &D
    }
}

fn takes_d_ref(_arg: &D) {}

fn main() {
    //takes_b_ref(&A);
    takes_d_ref(&C);
}

(Playground)

takes_b_ref(&A) will not work.

Yeah, I think it's Deref-coercion there, and the as &str restricts the type of the result (and sometimes the as syntax seems to enable coercions which are not performed implicitly). But I still find it a bit confusing because there are so many possible coercions.

2 Likes