Rust: Why passing unused (_) parameter to function changes the outcome - rust

Why below snippet errors out
fn print() -> &str
{
let name2 = "blah";
return &name2;
}
fn main() {
let x = print();
println!("{}", x);
}
but not this
fn print(_: &str) -> &str
{
let name2 = "blah";
return &name2;
}
fn main()
{
let x = print("");
println!("{}", x);
}
The only difference is the additional parameter which is ignored in the last case

The error message tells you quite clearly why the first snippet fails:
1 | fn print() -> &str
| ^ expected named lifetime parameter
you're returning a reference, references must have a lifetime to indicate their validity. You don't provide a lifetime and rustc's rules don't provide any either, so the compilation fails.
In the second snippet, you pass in a reference. Following lifetime elision:
If there is exactly one input lifetime position (elided or not), that lifetime is assigned to all elided output lifetimes.
rustc will use the input lifetime for the output, hence the second snippet is in essence
fn print(_: &'a str) -> &'a str
Since a string literal has lifetime 'static, it can be "cast down" to any lifetime (since they're all shorter), therefore returning name2 which is an &'static str does not break the contract: it will be valid for whatever the caller provides and wants.

In the first case, the declaration is missing the lifetime specifier.
You could fix this declaration with the lifetime you use:
fn print() -> &'static str {
let name2 = "blah";
return &name2;
}
In the second one, you're using implicit lifetimes (with Rust Edition 2018).
Implicit lifetimes really means the lifetime of the function parameter is the lifetime on output. It is thus specified.
What you're implicitly declaring is this:
fn print<'a>(_: &'a str) -> &'a str {
let name2 = "blah";
return &name2;
}
It "works" but only because both the argument given and the implementations are based on static lifetime. Otherwise you wouldn't be able to call it. It thus adds really nothing over the first fixed version.
more on implicit anonymous lifetimes

Related

Is this an error in the Rust lifetime system, or do I just not understand lifetimes

The following function is a typical example given when explaining lifetimes.
fn longest_string<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() >= s2.len() {
s1
} else {
s2
}
}
From the Rust documentation (https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html) we can read the following: "The concrete lifetime that is substituted for 'a is the part of the scope of s1 that overlaps with the scope of s2. In other words, the generic lifetime 'a will get the concrete lifetime that is equal to the smaller of the lifetimes of s1 and s2."
Given the above description, how is it that the following example compiles??
fn main() {
let res;
let s1 = "Hello";
{
let s2 = "World!!";
res = longest_string(s1, s2);
println!("{} is longer", res);
}
println!("Finished with {} and with {}", s1, res);
// lifetime of 'res' should be tied to s2, and thus have expired ??
}
It seems to me that the lifetime of 'res' should have ended when s2 went out of scope, and caused a compiler error on the last line, but that didn't happen. This compiles (and runs) without errors.
Can someone explain why?
Your example works, because strings literals in rust have type &'static str. So even that s2 is in separate block it has the same lifetime as s1. If you instead create String inside inner block and passed it's reference you would get en error that you expected.

How does one restrict a lifetime to a closure environment in rust?

I am calling closures with a fold function inside another closure. While I am intending for nothing in this some_closure function to live outside the closure environment, I am receiving an error that I am dropping a value while it is still borrowed.
I have tried removing all lifetime specifiers from some_closure, because I find the compiler is much smarter than myself at figuring out lifetimes, but I'm also finding no success in this (the compiler will always ask for lifetime specifiers leading up to the point of the shown example).
What I would desire to do here is to specify a lifetime restricted to the length of the closure inside the function, rather than the function itself. But I have a feeling that what I think is the problem may not actually be my problem, and that there is some gap in my understanding of lifetimes in closures.
I've tried to minimize the example as much as possible:
struct HoldStr<'a>(&'a str);
fn clone_slice_borrows_into_vec<'a>() -> impl Fn(&[&'a HoldStr]) -> Vec<&'a HoldStr<'a>> {
|slice| {
let mut temp = vec![];
temp.clone_from_slice(slice);
temp
}
}
fn clone_slice_borrows_into_vec_same<'a>() -> impl Fn(&[&'a HoldStr]) -> Vec<&'a HoldStr<'a>> {
// Same as last function for the sake of example, but one can assume it does something else
}
fn some_closure<'a>() -> impl Fn() {
|| {
let my_vec = vec![HoldStr("one"), HoldStr("two")];
let my_vec_holding_borrow: Vec<&'a HoldStr> = my_vec.iter().collect();
let called_closures: [Box<dyn Fn(&[&'a HoldStr]) -> Vec<&'a HoldStr<'a>>>; 2] = [
Box::new(clone_slice_borrows_into_vec()),
Box::new(clone_slice_borrows_into_vec_same())
];
let _result = called_closures
.iter()
.fold(my_vec_holding_borrow, |acc, closure| closure(&acc));
}
}
I would expect everything to be dropped by the end of the closure inside some_closure and for this to be fine, especially since I am specifying that lifetime 'a does not relate to anything the function itself returns. But it seems that the borrowed value is expected to live until the end of the function itself. I get this error:
error[E0597]: `my_vec` does not live long enough
--> src/lib.rs:61:51
|
## | fn some_closure<'a>() -> impl Fn() {
| -- lifetime `'a` defined here
...
## | let my_vec_holding_borrow: Vec<&'a HoldStr> = my_vec.iter().collect();
| ---------------- ^^^^^^ borrowed value does not live long enough
| |
| type annotation requires that `my_vec` is borrowed for `'a`
...
## | }
| - `my_vec` dropped here while still borrowed
I'd be happy to hear anything from how to resolve the error, to that I've been going about this the wrong way in the first place.
You need higher-rank trait bounds for your closure types:
fn clone_slice_borrows_into_vec() -> impl for<'a> Fn(&[&'a HoldStr]) -> Vec<&'a HoldStr<'a>> {
...
(Full code in the playground)
The lifetime 'a isn't fixed for your closure. It should return a vector of references with lifetime 'a for any input slice with references of this lifetime. Your code used an externally fixed lifetime instead, which could be chosen by the caller of clone_slice_borrows_into_vec().
If you have a funciton definition like
fn foo<'a>() -> &'a Foo
then it's basically always a mistake. This lets the caller request an arbitrary lifetime, and the function promises to create a reference of this lifetime out of thin air, which is only possible if it gets static references from some global storage, in which case it should simply return &'static Foo.

Why can I return a reference to an owned value of a function?

In chapter 19.2 of The Rust Programming Language, the following example compiles without any error. I found out from issue #1834 that there is a new lifetime elision rule that implicitly makes 's longer than 'c.
Although I couldn't find a detailed explanation of this new elision rule, I guess that it is not more than just an implicit version of the longer, more explicit constraint: <'c, 's: 'c>. I think however my confusion is probably not about this new elision rule but of course I could be wrong about this.
My understanding is, that parse_context takes ownership of context as it has not been borrowed but actually moved to the function. That alone implies to me that the lifetime of context should match the lifetime of the function it is owned by regardless of the lifetimes and constraint we defined in Context, and Parser.
Based on those definitions, the part where context outlives the temporary Parser makes perfect sense to me (after all, we defined a longer lifetime), but the part where the &str reference is not dropped when context goes out of scope at the end of parse_context and I can still safely return it -- makes me puzzled.
What have I missed? How can the compiler reason about the lifetime of the returned &str?
UPDATED EXAMPLE
struct Context<'s>(&'s str);
struct Parser<'c, 's>
{
context: &'c Context<'s>,
}
impl<'c, 's> Parser<'c, 's>
{
fn parse(&self) -> Result<(), &'s str>
{
Err(self.context.0)
}
}
fn parse_context(context: Context) -> Result<(), &str>
{
Parser { context: &context }.parse()
}
fn main()
{
let mut s = String::new();
s += "Avail";
s += "able?";
if let Err(text) = parse_context(Context(&s))
{
println!("{}", text);
}
}
You are not returning a reference to the owned value of the function. You are returning a copy of the reference passed in.
Your function is an elaborate version of the identity function:
fn parse_context(s: &str) -> &str {
s
}
In your real code, you take a reference to a struct containing a string slice, then another reference to the string slice, but all of those references are thrown away.
For example, there's an unneeded reference in parse:
fn parse(&self) -> Result<(), &'s str> {
Err( self.context.0)
// ^ no & needed
}
Additionally, if you enable some more lints, you'll be forced to add more lifetimes to your function signature, which might make things more clear:
#![deny(rust_2018_idioms)]
fn parse_context(context: Context<'_>) -> Result<(), &'_ str> {
Parser { context: &context }.parse()
}
See also:
What are Rust's exact auto-dereferencing rules?
Although I couldn't find a detailed explanation of this new elision rule,
T: 'a inference in structs in the edition guide.
struct Context<'s>(&'s str);
→ Values of type Context hold a string with some lifetime 's. This lifetime is implicitly at least as long as the lifetime of the context, but it may be longer.
struct Parser<'c, 's>
{
context: &'c Context<'s>,
}
→ Values of type Parser hold a a reference to context with some lifetime 'c. This context holds a string with some other lifetime 's.
impl<'c, 's> Parser<'c, 's>
{
fn parse(&self) -> Result<(), &'s str>
{
Err(self.context.0)
}
}
→ Function parse returns a value with lifetime 's, ie. with the same lifetime as the string that is stored inside the context, which is not the same as the lifetime of the context itself.
fn parse_context(context: Context) -> Result<(), &str>
{
Parser { context: &context }.parse()
}
→ I'm not sure exactly where this is specified, but obviously the compiler infers that the lifetime for the returned string is the same as the 's parameter used for the context. Note that even though the context itself is moved into parse_context, this only affects the context itself, not the string that it contains.
fn main()
{
let mut s = String::new();
→ Create a new string valid until the end of main
s += "Avail";
s += "able?";
if let Err(text) = parse_context(Context(&s))
→ Create a new context and move it into parse_context. It will automatically be dropped at the end of parse_context. It holds a reference to string s which is valid until the end of main and parse_context returns a string with the same lifetime as s ⇒ text is valid until the end of main.
{
println!("{}", text);
}
}
→ No problem: text is valid until the end of main.
Thank to the comments of Jmb and some bits of Shepmaster's answer it is indeed clear to me now that my confusion was about the RAII rules and the ownership.
That is, the string was created in the main scope, the anonymous Context instance is not taking ownership of the string it is only borrowing a reference, therefore even when the instance is dropped at the end of parse_context along with the borrowed reference, a reference copied to the Err object still exists and points to the existing string -- hence we are using the constrained lifetime variables and the compiler is able to reason about the lifetime of the internal string reference.

How to match against a &'static str in Rust

I am a Rust beginner and I can't solve this type problem. I have tried replacing &name with name, but the error "pattern &_ not covered" occurred.
fn get_project(name: &'static str) {
match &name {
"hi" => {},
}
}
fn main() {
let project = get_project("hi");
}
Compiler error:
error[E0308]: mismatched types
--> <anon>:3:9
|
3 | "hi" => {},
| ^^^^ expected &str, found str
|
= note: expected type `&&str`
= note: found type `&'static str`
String literals – like "hi" – have the type &'static str. So if you already have a &str, you don't need to add the &:
fn get_project(name: &str) {
match name {
"hi" => {},
_ => {}, // matches have to be exhaustive
}
}
I also added a default case, because matches in Rust need to be exhaustive: they need to cover all possible cases.
Maybe you noticed, that I also removed the 'static from the argument list. If you want to read about some lifetime stuff, go ahead. Else, stop reading here, because it's possibly confusing and not that important in this case.
In this function there is no need to restrict the lifetime of the given argument to 'static. Maybe you also want to pass in string slices that are borrowed from a String:
let user_input = read_user_input(); // type `String`
get_project(&input);
The code above only works when you remove the 'static from the argument. Once removed, the function is equivalent to:
fn get_project<'a>(name: &'a str) { ... }
This means that the function is generic over a lifetime 'a. The function says: given any lifetime 'a, you can give me a string with said lifetime and I am able to do my thing. Which is true. If the function wouldn't be able to do it for any lifetime, the compiler would complain ;-)
In your example, name doesn't need to have a static lifetime. Because you only use name inside your function, name doesn't need to have an extended lifetime. Check out the strings chapter of The Rust Programming Language. To match a &str with a &'static str you don't need &, just the variable itself is enough.
pub fn get_project(name: &str) {
match name {
"hi" => println!("I found hi!"),
_ => println!("Nothing match"),
}
}
fn main() {
get_project("hi");
get_project("42");
}

How do I get lifetime of reference to owned object? (cannot infer an appropriate lifetime for lifetime parameter)

How do I get this to compile?
extern crate collections;
fn print_x_from_table(table: collections::BTreeMap<String, String>) {
let x: &str = table
.get(&String::from_str("x"))
.map(|s: &String| &s[]) // ERROR!
.unwrap_or("");
println!("table contains x={}", x);
}
It gives this error:
src/rusttest.rs:5:22: 5:25 error: cannot infer an appropriate lifetime for lifetime parameter 'a in function call due to conflicting requirements
src/rusttest.rs:5 .map(|s: &String| &s[])
^~~
src/rusttest.rs:7:34: 7:35 note: first, the lifetime cannot outlive the expression at 7:33...
src/rusttest.rs:7 println!("table contains x={}", x);
^
note: in expansion of format_args!
<std macros>:2:43: 2:76 note: expansion site
<std macros>:1:1: 2:78 note: in expansion of println!
src/rusttest.rs:7:2: 7:37 note: expansion site
src/rusttest.rs:7:34: 7:35 note: ...so type `&str` of expression is valid during the expression
src/rusttest.rs:7 println!("table contains x={}", x);
If table were a reference parameter, I know that I could add a type parameter 'a to indicate how long s should live. But how do I do that when I own table?
extern crate collections;
fn print_x_from_ref_table<'a>(table: &'a collections::BTreeMap<String, String>) {
let x: &'a str = table
.get(&String::from_str("x"))
.map(|s: &'a String| &s[])
.unwrap_or("");
println!("table contains x={}", x);
}
I managed to get it working by avoiding using closures and using match instead. I think this works because in .map(|s| &s[]) the compiler gets confused and thinks the reference should live shorter than how long it actually lives for.
fn print_x_from_table(table: collections::BTreeMap<String, String>) {
let x = match table.get("x") {
Some(v) => &v[],
None => ""
};
println!("table contains x={}", x);
}
I think this is a limitation of the closure syntax. Check out these alternatives:
use std::ops::Deref;
use std::collections::HashMap;
fn inner<'a>(s: &'a String) -> &'a str { //'
&s[]
}
fn inner2(s: &String) -> &str {
&s[]
}
fn print_x_from_table(table: HashMap<String, String>) {
let x = table
.get("x")
// .map(|s: &String| &s[]) // ERROR!
// .map(inner) // OK
// .map(inner2) // OK
// .map(Deref::deref) // OK
.map(|s| &s[]) // OK
.unwrap_or("");
println!("table contains x={}", x);
}
fn main() {
}
In the second example, we use a function inner that has explicit named lifetimes that tie the input and the output together, letting the reference live beyond the closure. However, this is the exact case for lifetime elision, so inner2 is the same concept, but shorter. We could also cut out the middleman and just use the Deref::deref method directly.
The last example doesn't specify any types, so the compiler automatically inserts them and ties the references together (this last part is a guess based on observation, not intrinsic knowledge). This is probably what you'd see most of the time; it's not super common to specify types when you don't need to.
One potential "solution" would be if we could specify lifetimes ourselves on the closure definition. A hypothetical syntax like
<'a>|foo: &'a String| -> &'a str { foo.bar() }
could do the trick, but I don't know of any way to make that work.

Resources