String literal in struct

fn main() {
let value: &str = "abc";
let obj1 = PersonOne { name: value };
}
struct PersonOne<'a, T> {
name: &'a T,
}

why here obj1 type is PersonOne<',str> not PersonOne<',&str>

Because that's what's needed for types to match. For PersonOne<'_,&str> you'll need to write let obj1 = PersonOne { name: &value };

This is effectively the same code as asked about in this question yesterday. Where are you getting this code from? It doesn't seem to appear in The Book.

1 Like

Why do I need to specify it as '&value' again when the 'value' is already &str ?

Think of it like algebraic substitution. The & in the type signature means "shared reference to".

2 Likes

Because PersonOne<T> contains a field of type &T. If you want a PersonOne<&str>, then T = &str and so &T = &&str.

The generic parameter doesn't itself specify the type of the fields directly; the type declaration of the field does (unsurprisingly). There's no such rule that SomeGenericType<T> must contain fields of exactly type T; it can use and transform the generic paramters arbitarily – exactly as your type does.

1 Like
fn main() {
    let value: &str = "abc";
    let to = fun1(value);
}
fn fun1<T>(name: &T) -> &T {
    name
}

when function calls.why here value accepted instead of &value.

Because value already has a reference type.

In the previous case of struct PersonOne. here also value already has reference type

Yes, and? The generic struct case works exactly the same way as the generic function in terms of substituting type parameters. If the value with the generic type (field or argument) is specified to have type &T, and you pass a value of type &str, then T will be equated with str.

Let's walk through this in slower motion. The code you posted doesn't actually compile.

error[E0277]: the size for values of type `str` cannot be known at compilation time
 --> src/main.rs:3:19
  |
3 |     let to = fun1(value);
  |              ---- ^^^^^ doesn't have a size known at compile-time
  |              |
  |              required by a bound introduced by this call
  |
  = help: the trait `Sized` is not implemented for `str`
note: required by a bound in `fun1`
 --> src/main.rs:5:9
  |
5 | fn fun1<T>(name: &T) -> &T {
  |         ^ required by this bound in `fun1`
help: consider relaxing the implicit `Sized` restriction
  |
5 | fn fun1<T: ?Sized>(name: &T) -> &T {
  |          ++++++++

If you read through the error, it should be clear that T is being replaced with str in this call. This is the important part for your question; we'll get back to that in a moment.

As for the error, generic type parameters have an implicit Sized bound. That's usually what you want when taking or storing things by value, but when you have a reference or some other indirect pointer to a value, it's more flexible to take values which may not be Sized -- which don't have a size known at compile time -- things we call dynamically sized types (DSTs) or unsized. You can do this by removing the implicit bound by writing T: ?Sized like the help in the error says.

Learning about DSTs is its own topic, but that's all we need for the question at hand.


OK, now we've done that and it compiles. It's still the case that T is str in this call.

//                       v-vvv
let to = fun1(value /* : & str */);
fn fun1<T: ?Sized>(name: &  T    ) -> &T {
//                       ^-^^^

Or from your OP.

//                                     v---vvv
struct PersonOne<'a, T: ?Sized>{ name: &'a  T  }
let value:                             &   str = "abc";
//                                     ^---^^^
let obj1 = PersonOne { name: value };

A call to fun1::<T> takes a reference to a T. You're passing a &str -- a reference to a str. So you must be calling fun1::<str>; T must be str in this call.

A PersonOne<'_, T> stores a reference to a T. You're storing a &str -- a reference to a str. So you must be creating a PersonOne<'_, str>; T must be str for this variable.

2 Likes

So here we, finally, arrived at your real question. You are confused by the fact that function and data structure behave differently while everyone else are trying to understand why you confused when they behave identially.

Indeed there are subtle difference between two cases, but it's not where you are are expecting.

The Rule number zero of Rust's learning is: don't ignore error messages.

And, indeed, if you would see on your first program you'll notice that compiler is angry not because it couldn't determine what T to pick, but because T that it picked, str, is not good!

Now, let's become explicit shell we?

fn main() {
    let value: &str = "abc";
    let obj1 = PersonOne { name: value };
    let to = fun1(value);
}
struct PersonOne<'a, T: ?Sized> {
    name: &'a T,
}
fn fun1<T: ?Sized>(name: &T) -> &T {
    name
}

This works. Both functions and data structure behave the same.

fn main() {
    let value: &str = "abc";
    let obj1 = PersonOne { name: value };
    let to = fun1(value);
}
struct PersonOne<'a, T: !Sized> {
    name: &'a T,
}
fn fun1<T: !Sized>(name: &T) -> &T {
    name
}

This doesn't work. Both functions and data structure behave the same

And I, apparently, forgot about exact rules when ?sized is implicit.

1 Like

This does not compile. You have to add the ?Sized trait bound, i.e. fn fun1<T: ?Sized>(name: &T).

As others have stated, when you pass &str to a function that expects &T, then T = str.

The error message is already telling you what's wrong and how to fix it:

fn fun1<T>(name: &T) -> &T {
  |         ^ required by this bound in `fun1`
help: consider relaxing the implicit `Sized` restriction

Make emphasis on the word implicit. It means that there's an implicit bound on T to implement the Sized trait. If you write this:

fn fun1<T: ?Sized>(name: &T) -> &T {
    name
}

You are telling the compiler that T may or may not implement the Sized trait, thus it compiles again.

BTW, in structs you almost always want to use owning types like String or Box<str>, instead of temporary scope-bound references. Structs with lifetime are a special case. They become only temporarily valid, strictly bound to a scope, and this makes them very inflexible and limited in usefulness.

PersonOne {
     name: String, // can live as long as it wants to
}

This allows the struct to be freely used anywhere, and you don't have to deal with very limiting lifetime annotations.

If you support only string literals, then use:

PersonOne {
     name: &'static str, // only literals and leaked memory, all other strings forbidden
}

or support both literals (without allocation) and other strings:

PersonOne {
     name: Cow<'static, str>,
}

If you want the string type to be generic, use a generic type without forcing it to be a temporary reference:

PersonOne<T> {
     name: T,
}

// It's easier to put bounds like AsRef<str> is on the impl, 
// rather than the struct. 
impl<T: AsRef<str>> for PersonOne<T> {
    pub fn hi(&self) {
        println!("hi, I'm {}", self.name.as_ref());
    } 
}

Note that in all of these cases, there's no 'a on the struct.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.