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>
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.
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".
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.
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.
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.
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.