Как я понял, дать гарантии компилятору, для того, чтобы сделать то, что вы хотите, пока нельзя, или сделать это будет очень неочевидно. Гарантии нужны потому что язык безопасный. Возможно когда добавят кастомные маркеры в язык, тогда можно будет чё-то гарантировать. А пока можно пользоваться макросами. Это не так плохо как кажеться. Это не нагружает бинарник, нагружает только компилятор. Причем линейно. Нагрузка на компилятор будет составлять (Количество типов * количество функций). Правда если будет использоваться больше одного типа в структуре, тогда будет применятся комбинаторика для определения нагрузки на компилятор. То есть само приложение будет таким же производительным, правда из-за нагрузки на компилятор, он может перестать немного оптимизировать код. Вот решение чтобы не дублировать код:
mod float_trait {
pub trait Float {}
impl Float for f32 {}
impl Float for f64 {}
}
use crate::float_trait::Float;
mod float_struct {
use crate::float_trait::Float;
#[derive(Debug)]
pub struct FloatStruct<T: Float>
{
_a: i8,
_b: String,
pub c: T,
}
macro_rules! impl_float_struct {
($($type:ty),*) => {
$(
impl FloatStruct<$type> {
pub fn new() -> Self {
Self {
_a: 2,
_b: String::from("hi"),
c: 0.3,
}
}
}
)*
}
}
impl_float_struct![f32, f64];
}
fn main() {
let fs = float_struct::FloatStruct::<f32>::new();
println!("{:#?}", fs.c)
}
Другое решение, очевидно нагружающее само приложение, и лёгкое для компилятора
mod float_trait {
pub trait Float {}
impl Float for f32 {}
impl Float for f64 {}
}
mod float_struct {
use crate::float_trait::Float;
#[derive(Debug)]
pub struct FloatStruct<T: std::str::FromStr + Float>
{
_a: i8,
_b: String,
pub c: T,
}
impl<T: std::str::FromStr + Float> FloatStruct<T> {
pub fn new() -> Self {
Self {
_a: 2,
_b: String::from("hi"),
c: {
let Ok(r) = "0.3".parse::<T>()
else {unreachable!()};
r
}
}
}
}
}
fn main() {
let fs = float_struct::FloatStruct::<f32>::new();
println!("{:#?}", fs.c)
}