I've been playing around with Rust by following along with Roguelike Tutorial, and have started to branch out a bit in hopes of creating some kind of nature simulation.
In my simple POC, I'm trying to have multiple "Creatures" wandering the map looking for entities with the ProvidesHealth component (so like plants or bushes or something that get eaten).
In the Roguelike tutorial, monsters can easily locate the player at all times because the player is shared throughout the world as a resource, but in my case, I can't figure out the best way to simulate this behavior in my Specs system.
The Creature entities have a Viewshed component to act as their vision. I originally thought I'd be able to iterate thru the Viewshed's visible_tiles and check if an entity with the ProvidesHealth entity was there, but I wasn't able to get that work.
Any thoughts on this would be greatly appreciated! I'm not sure if my approach is totally off, or I'm missing something simple.
Thanks!
// [dependencies]
// bracket-lib = { git = "https://github.com/thebracket/bracket-lib.git", rev = "927d229" }
// specs = "0.16.1"
// specs-derive = "0.4.1"
use bracket_lib::prelude::*;
use specs::prelude::*;
use specs_derive::*;
use std::{thread, time};
#[derive(Component)]
struct Position {
x: i32,
y: i32,
}
#[derive(Component)]
struct Renderable {
glyph: FontCharType,
fg: RGB,
bg: RGB,
}
#[derive(Component)]
struct Creature {}
#[derive(Component)]
struct ProvidesHealth {
pub hp_gain: i32
}
#[derive(Component)]
pub struct Viewshed {
pub visible_tiles: Vec<Point>,
pub range: i32,
pub dirty: bool
}
struct State {
ecs: World
}
impl State {
fn run_systems(&mut self) {
let mut vis = VisSystem {};
vis.run_now(&self.ecs);
let mut ai = CreatureAI {};
ai.run_now(&self.ecs);
self.ecs.maintain();
}
}
impl GameState for State {
fn tick(&mut self, ctx: &mut BTerm) {
ctx.cls();
self.run_systems();
// map defined in separate file, but isn't really
// important for this question
// draw_map(&self.ecs, ctx);
let positions = self.ecs.read_storage::<Position>();
let renderables = self.ecs.read_storage::<Renderable>();
for (pos, ren) in (&positions, &renderables).join() {
ctx.set(pos.x, pos.y, ren.fg, ren.bg, ren.glyph);
}
let sleep = time::Duration::from_millis(200);
thread::sleep(sleep);
}
}
fn main() -> BError {
let mut context = BTermBuilder::simple80x50()
.build()?;
let mut gs = State {
ecs: World::new(),
};
gs.ecs.register::<Position>();
gs.ecs.register::<Renderable>();
gs.ecs.register::<Creature>();
gs.ecs.register::<ProvidesHealth>();
gs.ecs.register::<Viewshed>();
// add one Creature
gs.ecs
.create_entity()
.with(Position {x: 10, y: 20})
.with(Renderable {
glyph: to_cp437('#'),
fg: RGB::named(WHITE),
bg: RGB::named(BLACK)
})
.with(Creature {})
.with(Viewshed { visible_tiles : Vec::new(), range: 6, dirty: true })
.with(HealthStats { max_hp: 100, hp: 100 })
.build();
// add one "food" item
gs.ecs
.create_entity()
.with(Position {x: 35, y: 35})
.with(Renderable {
glyph: to_cp437('*'),
fg: RGB::named(WHITE),
bg: RGB::named(BLACK),
})
.with(ProvidesHealth { hp_gain: 10 })
.build();
// map defined in separate file, but isn't really
// important for this question
let mut map = Map::new_map();
gs.ecs.insert(map);
main_loop(context, gs)
}
struct VisSystem {}
impl<'a> System<'a> for VisSystem {
type SystemData = (
WriteExpect<'a, Map>,
Entities<'a>,
WriteStorage<'a, Viewshed>,
ReadStorage<'a, Position>,
);
fn run(&mut self, data: Self::SystemData) {
let (map, entities, mut viewshed, pos) = data;
for (_ent, viewshed, pos) in (&entities, &mut viewshed, &pos).join() {
if viewshed.dirty {
viewshed.visible_tiles = field_of_view(
Point::new(pos.x, pos.y),
viewshed.range,
&*map,
)
}
}
}
}
struct CreatureAI {}
impl<'a> System<'a> for CreatureAI {
#[allow(clippy::type_complexity)]
type SystemData = (
// ...
);
fn run(&mut self, data: Self::SystemData) {
// ... not sure what to do here\
//
// by doing a join on (viewshed, position),
// i'd be able to iterate thru viewshed.visible_tiles,
// but i can't figure out how I could check if a given
// entity located at the Point has the "ProvidesHealth"
// component or not
}
}
Related
I want to capture the duration of execution of a span in rust tracing and send that as metric.
I have found that fmt() helps in printing that as mentioned here:How can I log span duration with Rust tracing?
I have also tried this example about creating layer and implementing on_new_span() and on_event(). I added on_close() as well to check what metadata do we get here. The code for that I wrote is:
use tracing::{info, info_span};
use tracing_subscriber::prelude::*;
mod custom_layer;
use custom_layer::CustomLayer;
fn main() {
tracing_subscriber::registry()
.with(CustomLayer)
.init();
let outer_span = info_span!("Outer", level = 0, other_field = tracing::field::Empty);
let _outer_entered = outer_span.enter();
outer_span.record("other_field", &7);
let inner_span = info_span!("inner", level = 1);
let _inner_entered = inner_span.enter();
info!(a_bool = true, answer = 42, message = "first example");
}
custom_layer.rs:
use std::collections::BTreeMap;
use tracing_subscriber::Layer;
pub struct CustomLayer;
impl<S> Layer<S> for CustomLayer
where
S: tracing::Subscriber,
S: for<'lookup> tracing_subscriber::registry::LookupSpan<'lookup>,
{
fn on_new_span(
&self,
attrs: &tracing::span::Attributes<'_>,
id: &tracing::span::Id,
ctx: tracing_subscriber::layer::Context<'_, S>,
) {
let span = ctx.span(id).unwrap();
let mut fields = BTreeMap::new();
let mut visitor = JsonVisitor(&mut fields);
attrs.record(&mut visitor);
let storage = CustomFieldStorage(fields);
let mut extensions = span.extensions_mut();
extensions.insert(storage);
}
fn on_record(
&self,
id: &tracing::span::Id,
values: &tracing::span::Record<'_>,
ctx: tracing_subscriber::layer::Context<'_, S>,
) {
// Get the span whose data is being recorded
let span = ctx.span(id).unwrap();
// Get a mutable reference to the data we created in new_span
let mut extensions_mut = span.extensions_mut();
let custom_field_storage: &mut CustomFieldStorage =
extensions_mut.get_mut::<CustomFieldStorage>().unwrap();
let json_data: &mut BTreeMap<String, serde_json::Value> = &mut custom_field_storage.0;
// And add to using our old friend the visitor!
let mut visitor = JsonVisitor(json_data);
values.record(&mut visitor);
}
fn on_event(&self, event: &tracing::Event<'_>, ctx: tracing_subscriber::layer::Context<'_, S>) {
// All of the span context
let scope = ctx.event_scope(event).unwrap();
let mut spans = vec![];
for span in scope.from_root() {
let extensions = span.extensions();
let storage = extensions.get::<CustomFieldStorage>().unwrap();
let field_data: &BTreeMap<String, serde_json::Value> = &storage.0;
spans.push(serde_json::json!({
"target": span.metadata().target(),
"name": span.name(),
"level": format!("{:?}", span.metadata().level()),
"fields": field_data,
}));
}
// The fields of the event
let mut fields = BTreeMap::new();
let mut visitor = JsonVisitor(&mut fields);
event.record(&mut visitor);
// And create our output
let output = serde_json::json!({
"target": event.metadata().target(),
"name": event.metadata().name(),
"level": format!("{:?}", event.metadata().level()),
"fields": fields,
"spans": spans,
});
println!("{}", serde_json::to_string_pretty(&output).unwrap());
}
fn on_close(
&self,
id: tracing::span::Id,
ctx: tracing_subscriber::layer::Context<'_, S>,
) {
// Get the span whose data is being recorded
let span = ctx.span(&id).unwrap();
let output = serde_json::json!({
"target": span.metadata().target(),
"name": span.name(),
"level": format!("{:?}", span.metadata().level()),
"fields": format!("{:?}", span.metadata().fields()),
});
println!("On_close{}", serde_json::to_string_pretty(&output).unwrap());
}
}
struct JsonVisitor<'a>(&'a mut BTreeMap<String, serde_json::Value>);
impl<'a> tracing::field::Visit for JsonVisitor<'a> {
fn record_f64(&mut self, field: &tracing::field::Field, value: f64) {
self.0
.insert(field.name().to_string(), serde_json::json!(value));
}
fn record_i64(&mut self, field: &tracing::field::Field, value: i64) {
self.0
.insert(field.name().to_string(), serde_json::json!(value));
}
fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
self.0
.insert(field.name().to_string(), serde_json::json!(value));
}
fn record_bool(&mut self, field: &tracing::field::Field, value: bool) {
self.0
.insert(field.name().to_string(), serde_json::json!(value));
}
fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
self.0
.insert(field.name().to_string(), serde_json::json!(value));
}
fn record_error(
&mut self,
field: &tracing::field::Field,
value: &(dyn std::error::Error + 'static),
) {
self.0.insert(
field.name().to_string(),
serde_json::json!(value.to_string()),
);
}
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
self.0.insert(
field.name().to_string(),
serde_json::json!(format!("{:?}", value)),
);
}
}
#[derive(Debug)]
struct CustomFieldStorage(BTreeMap<String, serde_json::Value>);
Cargo.toml
[package]
name = "tracing-custom-logging"
version = "0.1.0"
edition = "2021"
[dependencies]
serde_json = "1"
tracing = "0.1"
tracing-subscriber = "0.3.16"
snafu = "0.7.3"
thiserror = "1.0.31"
tracing-opentelemetry = "0.18.0"
Unfortunately I have not been able to get the data about duration of a span anywhere. Can you guys help me identify how/where can I get it from?
You cannot "get" the span duration from the tracing crate because it doesn't store it. It only stores the basic metadata and allows for hooking into framework events in a lightweight way. It is the job of the Subscriber to keep track of any additional data.
You could use the tracing-timing crate if you only need periodic histograms. Otherwise, you can't really use data from an existing layer which may already store timing data, because they often don't expose it. You'll have to keep track of it yourself.
Using the tracing-subscriber crate, you can create a Layer and store additional data using the Registry. Here's an example of how that can be done:
use std::time::Instant;
use tracing::span::{Attributes, Id};
use tracing::Subscriber;
use tracing_subscriber::layer::{Context, Layer};
use tracing_subscriber::registry::LookupSpan;
struct Timing {
started_at: Instant,
}
pub struct CustomLayer;
impl<S> Layer<S> for CustomLayer
where
S: Subscriber,
S: for<'lookup> LookupSpan<'lookup>,
{
fn on_new_span(&self, _attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
let span = ctx.span(id).unwrap();
span.extensions_mut().insert(Timing {
started_at: Instant::now(),
});
}
fn on_close(&self, id: Id, ctx: Context<'_, S>) {
let span = ctx.span(&id).unwrap();
let started_at = span.extensions().get::<Timing>().unwrap().started_at;
println!(
"span {} took {}",
span.metadata().name(),
(Instant::now() - started_at).as_micros(),
);
}
}
This just prints out the results where they are calculated, but you can emit the results elsewhere, or store it in some shared resource as you see fit.
Some example usage:
use std::time::Duration;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
#[tracing::instrument]
fn test(n: u64) {
std::thread::sleep(Duration::from_secs(n));
}
fn main() {
tracing_subscriber::registry::Registry::default()
.with(CustomLayer)
.init();
test(1);
test(2);
test(3);
}
span test took 1000081
span test took 2000106
span test took 3000127
You may also need to be aware of on_enter() and on_exit(), which are relevant when using async functions because their execution may be suspended and resumed later, and you can use those functions to be notified when that happens. Depending on what you're looking for, you may need to add filtering so that you only track the spans you're interested in (by name or target or whatever).
I am just a beginner in Rust and so far I have managed to obey borrow-checker's warnings in a async-heavy tokio app until now.
Basically I have a struct which has a HashMap of games. I want to insert a created game into the hashmap and at the same time pass it to a game_loop that I spawn with tokio::spawn. Game loop will update the game but I also want to be able to retrieve the game from the hashmap to execute some functions to check its state etc.
I've tried wrapping it in Arc and Mutexes and whatnot. At the moment I just clone it to the game_loop but - as smarter people probably know - that will only pass a clone of the original and the entity in the hashmap wont update.
GameManager
pub struct GameManager {
games: HashMap<Uuid, Game>,
}
impl GameManager {
fn find_or_create_game(&mut self, user_options: &GameOptions) -> Uuid {
for g in self.games.values() {
println!("game id {:?}", g.id);
println!("game players {:?}", g.state.get_players());
if g.allows_joining() && g.matches_player_options(user_options) {
println!("Joining existing game");
return g.id;
}
}
let rng = ::rand::rngs::StdRng::from_seed(OsRng.gen());
let mut game = Game::new(Some(user_options.clone()), rng);
let game_id = game.id;
let (game_sender, game_receiver) = mpsc::unbounded_channel::<GameEvent>();
let broadcast = self.broadcast.clone();
self.game_channels.insert(game_id, game_sender.clone());
self.games.insert(game_id, game);
tokio::spawn(game_loop(game, broadcast, game_receiver));
return game_id;
}
}
game_loop
pub async fn game_loop(
mut game: Game,
broadcast: UnboundedSender<ServerEvent>,
mut receiver: UnboundedReceiver<GameEvent>,
) -> Result<(), io::Error> {
let dur = std::time::Duration::from_secs_f64(1.0 / game.state.options.fps as f64);
let mut interval = tokio::time::interval(dur);
loop {
interval.tick().await;
while let Some(is_event) = unconstrained(receiver.recv()).now_or_never() {
if let Some(event) = is_event {
handle_game_event(event, &mut game, &broadcast);
}
}
if game.has_ended() {
break;
} else {
game.tick();
let _ = broadcast.send(ServerEvent::Tick(game.get_tick()));
}
}
Ok(())
}
Okay yeah, I am a dummy. Thanks #Peterrabbit though for giving me the motivation to try using Arc again. Had to wrap it in a mutex but all together, I am just happy that it now works.
So now it is:
pub struct GameManager {
games: HashMap<Uuid, Arc<Mutex<Game>>>,
}
impl GameManager {
fn find_or_create_game(&mut self, user_options: &GameOptions) -> Uuid {
for g in self.games.values() {
if g.allows_joining() && g.matches_player_options(user_options) {
println!("Joining existing game");
return g.id;
}
}
let rng = ::rand::rngs::StdRng::from_seed(OsRng.gen());
let mut game = Arc::new(Mutex::new(Game::new(Some(user_options.clone()), rng)));
let game_id = game.lock().await.id;
let (game_sender, game_receiver) = mpsc::unbounded_channel::<GameEvent>();
let broadcast = self.broadcast.clone();
self.game_channels.insert(game_id, game_sender.clone());
self.games.insert(game_id, game.clone());
tokio::spawn(game_loop(game.clone(), broadcast, game_receiver));
return game_id;
}
}
pub async fn game_loop(
mut game: Arc<Mutex<Game>>,
broadcast: UnboundedSender<ServerEvent>,
mut receiver: UnboundedReceiver<GameEvent>,
) -> Result<(), io::Error> {
let dur = std::time::Duration::from_secs_f64(1.0 / game.lock().await.state.options.fps as f64);
let mut interval = tokio::time::interval(dur);
loop {
interval.tick().await;
while let Some(is_event) = unconstrained(receiver.recv()).now_or_never() {
if let Some(event) = is_event {
handle_game_event(event, &mut game, &broadcast).await;
}
}
if game.lock().await.has_ended() {
break;
} else if game.lock().await.is_running() {
handle_game_event(GameEvent::Tick(), &mut game, &broadcast).await;
}
}
Ok(())
}
I want to use a function in the main function in a rust program that I am building to help me learn rust and come up with an error: self value is a keyword only available in methods with a self parameterrustc(E0424). What can I fix in my code so that this error does not happen?
pub use crate::user_account::user_account;
use rand::Rng;
#[allow(dead_code)]
pub trait UserInfo {
fn user_info(&mut self);
fn acc_no(&mut self);
fn yes(self);
fn bank_new_user(self);
}
pub struct NewUser {
age: String,
new_user: String,
account: String,
account_number: i32,
routing_number: i32,
select: String,
}
impl UserInfo for NewUser {
fn user_info(&mut self) {
self.age = String::new();
self.new_user = String::new();
println!("What is your name?");
print!("Name: ");
std::io::stdin().read_line(&mut self.new_user);
println!(" ");
println!("Hello {}, What is your age? ", self.new_user);
std::io::stdin().read_line(&mut self.age);
let age2: String = self.age.trim().into();
}
fn acc_no(&mut self) {
println!(
"We will generate a new account number \
and routing number for you."
);
self.account_number = rand::thread_rng().gen_range(10000000..99999999);
println!("Your account number is {}", self.account_number);
self.routing_number = rand::thread_rng().gen_range(10000000..99999999);
println!("Your account routing number is {}", self.routing_number);
}
fn yes(self) {
NewUser::user_info(&mut self);
NewUser::acc_no(&mut self);
}
//function I want to use in main.
fn bank_new_user(self) {
self.account = String::new();
println!("Would you like to make a new account with us today?");
loop {
println!(
" yes: continue to application, no: continue browsing , \
or exit: to exit"
);
self.account.clear();
std::io::stdin()
.read_line(&mut self.account)
.expect("please type yes, no or exit.");
let account = self.account.trim();
match account {
"yes" => {
self.yes();
break;
}
"no" => {
println!("You do not need an account to continue browsing.");
println!("Have a wonderful day and thank you for considering Mars Banking!");
break;
}
"exit" => {
println!(
"Thank you for choosing Mars Banking for your banking needs!\
Have a wonderful day!"
);
break;
}
_ => {
println!("Error! Enter yes, no, or exit.")
}
}
}
}
}
pub mod new_user;
mod settings;
mod user_account;
pub use crate::settings::settings;
pub use crate::user_account::user_account;
use new_user::NewUser;
use new_user::UserInfo;
fn main() {
loop{
let mut select = String::new();
println!("Welcome to Mars Banking!");
println!("What would you like to do today?");
println!("Create a new account: 1\nLogin: 2\nSettings: 3\nExit: 4");
select.clear();
std::io::stdin().read_line(&mut select);
let select = select.trim();
match select {
//Here is where the error happens.
"1" => NewUser::bank_new_user(self),
"2" => user_account(),
"3" => settings(),
"4" => break,
_ => {}
}
}
}
The conventional pattern for this sort of constructor is a static method that doesn't take a self argument, like this:
impl NewUser {
fn bank_new_user() {
let mut new_user = NewUser { /* initialize the fields */ };
// Edit or use new_user as necessary
}
}
you can see an example of this here, in the methods defined for Point:
struct Point {
x: f64,
y: f64,
}
// Implementation block, all `Point` associated functions & methods go in here
impl Point {
// This is an "associated function" because this function is associated with
// a particular type, that is, Point.
//
// Associated functions don't need to be called with an instance.
// These functions are generally used like constructors.
fn origin() -> Point {
Point { x: 0.0, y: 0.0 }
}
// Another associated function, taking two arguments:
fn new(x: f64, y: f64) -> Point {
Point { x: x, y: y }
}
}
notice how niether origin nor new take self as an argument.
I have a struct with two Vecs wrapped in RefCells. I want to have a method on that struct that combines the two vectors and returns them as a new RefCell or RefMut:
use std::cell::{RefCell, RefMut};
struct World {
positions: RefCell<Vec<Option<Position>>>,
velocities: RefCell<Vec<Option<Velocity>>>,
}
type Position = i32;
type Velocity = i32;
impl World {
pub fn new() -> World {
World {
positions: RefCell::new(vec![Some(1), None, Some(2)]),
velocities: RefCell::new(vec![None, None, Some(1)]),
}
}
pub fn get_pos_vel(&self) -> RefMut<Vec<(Position, Velocity)>> {
let mut poses = self.positions.borrow_mut();
let mut vels = self.velocities.borrow_mut();
poses
.iter_mut()
.zip(vels.iter_mut())
.filter(|(e1, e2)| e1.is_some() && e2.is_some())
.map(|(e1, e2)| (e1.unwrap(), e2.unwrap()))
.for_each(|elem| println!("{:?}", elem));
}
}
fn main() {
let world = World::new();
world.get_pos_vel();
}
How would I return the zipped contents of the vectors as a new RefCell? Is that possible?
I know there is RefMut::map() and I tried to nest two calls to map, but didn't succeed with that.
You want to be able to modify the positions and velocities. If these have to be stored in two separate RefCells, what about side-stepping the problem and using a callback to do the modification?
use std::cell::RefCell;
struct World {
positions: RefCell<Vec<Option<Position>>>,
velocities: RefCell<Vec<Option<Velocity>>>,
}
type Position = i32;
type Velocity = i32;
impl World {
pub fn new() -> World {
World {
positions: RefCell::new(vec![Some(1), None, Some(2)]),
velocities: RefCell::new(vec![None, None, Some(1)]),
}
}
pub fn modify_pos_vel<F: FnMut(&mut Position, &mut Velocity)>(&self, mut f: F) {
let mut poses = self.positions.borrow_mut();
let mut vels = self.velocities.borrow_mut();
poses
.iter_mut()
.zip(vels.iter_mut())
.filter_map(|pair| match pair {
(Some(e1), Some(e2)) => Some((e1, e2)),
_ => None,
})
.for_each(|pair| f(pair.0, pair.1))
}
}
fn main() {
let world = World::new();
world.modify_pos_vel(|position, velocity| {
// Some modification goes here, for example:
*position += *velocity;
});
}
If you want to return a new Vec, then you don't need to wrap it in RefMut or RefCell:
Based on your code with filter and map
pub fn get_pos_vel(&self) -> Vec<(Position, Velocity)> {
let mut poses = self.positions.borrow_mut();
let mut vels = self.velocities.borrow_mut();
poses.iter_mut()
.zip(vels.iter_mut())
.filter(|(e1, e2)| e1.is_some() && e2.is_some())
.map(|(e1, e2)| (e1.unwrap(), e2.unwrap()))
.collect()
}
Alternative with filter_map
poses.iter_mut()
.zip(vels.iter_mut())
.filter_map(|pair| match pair {
(Some(e1), Some(e2)) => Some((*e1, *e2)),
_ => None,
})
.collect()
You can wrap it in RefCell with RefCell::new, if you really want to, but I would leave it up to the user of the function to wrap it in whatever they need.
In the past this code compiled, but recently it no longer is accepted (I believe since RFC 738). I want to type-parameterize the VertexBuffer, but it doesn't actually hold any vertices, rather the GPU holds the vertices, and the struct only holds an OpenGL buffer_id:
pub struct VertexBuffer<V: Vertex> {
buffer_id: GLuint,
num_vertices: usize,
}
The new function fills the buffer:
impl<V: Vertex> VertexBuffer<V> {
pub fn new(data: &Vec<V>) -> VertexBuffer<V>
{
let buffer_id = unsafe {
let mut id: GLuint = 0;
gl::GenBuffers(1, &mut id);
gl::BindBuffer(gl::ARRAY_BUFFER, id);
gl::BufferData(gl::ARRAY_BUFFER,
(mem::size_of::<V>() * data.len()) as GLsizeiptr,
mem::transmute(&data[0]),
gl::STATIC_DRAW);
id
};
VertexBuffer {
buffer_id: buffer_id,
num_vertices: data.len(),
}
}
....
}
I now get these errors:
src/vertex_buffer.rs:10:25: 10:26 error: parameter `V` is never used
src/vertex_buffer.rs:10 pub struct VertexBuffer<V: Vertex> {
src/vertex_buffer.rs:10:25: 10:26 help: consider removing `V` or using a marker such as `core::marker::PhantomData`
src/vertex_buffer.rs:10 pub struct VertexBuffer<V: Vertex> {
FYI, other functions in the impl such as pre_render() and post_render() use the V type to do their work, calling things like
let attribute_data = Vertex::attribute_data(None::<V>);
You haven't shown enough code to rule this out, so I'd suggest moving your type to the function(s):
impl VertexBuffer {
pub fn new<V: Vertex>(data: &Vec<V>) -> VertexBuffer<V> {
let buffer_id = unsafe {
let mut id: GLuint = 0;
gl::GenBuffers(1, &mut id);
gl::BindBuffer(gl::ARRAY_BUFFER, id);
gl::BufferData(gl::ARRAY_BUFFER,
(mem::size_of::<V>() * data.len()) as GLsizeiptr,
mem::transmute(&data[0]),
gl::STATIC_DRAW);
id
};
VertexBuffer {
buffer_id: buffer_id,
num_vertices: data.len(),
}
}
....
}
If you do need to use PhantomData, try something like:
struct VertexBuffer<V> {
buffer_id: u32, // or whatever
num_vertices: u32, // or whatever
marker: std::marker::PhantomData<V>,
}
impl<V: Vertex> VertexBuffer<V> {
pub fn new(data: &Vec<V>) -> VertexBuffer<V> {
let buffer_id = unsafe {
let mut id: GLuint = 0;
gl::GenBuffers(1, &mut id);
gl::BindBuffer(gl::ARRAY_BUFFER, id);
gl::BufferData(gl::ARRAY_BUFFER,
(mem::size_of::<V>() * data.len()) as GLsizeiptr,
mem::transmute(&data[0]),
gl::STATIC_DRAW);
id
};
VertexBuffer {
buffer_id: buffer_id,
num_vertices: data.len(),
marker: std::marker::PhantomData,
}
}
....
}