简体   繁体   中英

Is there any way to implement Into<&str> or From<T> for &str?

Was writing some code in Rust trying to define a CLI, using the (otherwise pretty great) crate clap , and run into a rather critical issue. Methods of App accept an Into<&'help str> , and i've been unable to find a way to implement this trait.

Indeed from what i understand, it is absolutely unimplementable:

struct JustWorkDamnIt {
    string: String
}

impl From<JustWorkDamnIt> for &str {
    fn from(arg: JustWorkDamnIt) -> Self {
        return arg.string.as_str()
    }
}

...which yields:

error[E0515]: cannot return value referencing local data `arg.string`
  --> src/cmd/interactive.rs:25:16
   |
25 |         return arg.string.as_str()
   |                ----------^^^^^^^^^
   |                |
   |                returns a value referencing data owned by the current function
   |                `arg.string` is borrowed here

Interestingly enough, however, returning a literal compiles just fine (which i reckon is why clap doesn't mind using this trait). Presumably that's because the literal is compiled to some static region of memory and therefore isn't owned by the function:

impl From<JustWorkDamnIt> for &str {
    fn from(arg: JustWorkDamnIt) -> Self {
        return "literal"
    }
}

But, i mean, surely there's a way to implement this trait and return dynamic strings? Maybe some clever use of Box<> or something, idk. I believe i've tried all the things i could think of.

And if there isn't a way, then this seems like a pretty glaring flaw for Rust - traits which can be declared and used in function headers, but cannot be actually implemented meaningfully (there's not much of a point in returning a literal). If this turns out to be the case i'll create an issue on the rust-lang repository for this flow, but first i wanted to sanity-check my findings here.


UPD: Thanks for the comments, made me think more in-depth about this issue.

The problem has to do with lifetimes, it seems. I apologize for not showing the entire code, i thought the snippets i shared would describe the problem sufficiently, but in hindsight it does make sense that the context would be important with Rust's lifetimes at play.

I did find a "solution" for this particular instance of the problem. Since the code in question will only run once per executable start, i can just Box::leak the &'static str and call it a day. Still, i would like to figure out if there's a more general solution which could be used for often-created dynamic strings.

impl Cmd for InteractiveCmd {
    fn build_args_parser<'a, 'b>(&'b self, builder: App<'a>) -> App<'a> {
        // leak() works here but i'm curious if there's a better way
        let staticStr : &'static str = Box::leak(Box::from(format!("Interactive {} shell", APPNAME).as_str()));
        let rv = builder
            // pub fn about<S: Into<&'b str>>(mut self, about: S) -> Self
            .about(staticStr) 
            .version("0.1");
        return rv;
    }
}

fn main() {
    let interactive = InteractiveCmd::new();
    let mut app = App::new(APPNAME)
        .version(APPVER)
        .author(AUTHORS)
      .subcommand(interactive.build_args_parser(App::new("interactive")));
}

Currently i am faced with 2 points of confusion here:

  • Why is there no impl From<String> for &str ? The compiler claims that one exists for impl From<String> for &mut str , and i'm not seeing the importance of mut here.
  • ...and if there is a good reason to not impl From<String> for &str , would it make sense to somehow discourage the usage of Into<&str> in the public APIs of libraries?

Or maybe the issue is with my build_args_parser function and it's just not an option to offload the work to it as far as Rust is concerned?

Seems like you're trying to convert a String into a &'a str . You can do it like this:

use clap::App;

fn main() {
    let s = format!("Interactive {} shell", "Testing");
    let app = App::new("Testing")
        .about(&*s); // or `.about(s.as_str());` it's the same
}

Here's the signature of the function that we want to call:

pub fn about<S: Into<&'b str>>(self, about: S) -> Self

So the about parameter must implement trait Into<&'b str> . According to std::convert::Into , we know that &'b str does implement trait Into<&'b str> . So now we just need a way to convert String into &'b str . The std::string::String tell us that there are two ways: use either s.as_str() or &*s .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM